Re: Error during SQL Server restore fails to propagate back to DBI client on Windows

[email protected] ("Martin J. Evans")
Newsgroups perl.dbi.users
Organization Easysoft Limited
Message-ID <[email protected]>
On 22/02/2010 19:27, pr wrote:
<huge amount snipped as I cannot keep track of this so I assume no one 
else can and I suspect we are getting close to some maximum post size>

>> Can I just wind back a bit as each time you email the
>> number of things
>> you seem unhappy with seems to grow.
>>
>> Starting with some facts (as I believe they to be):
>>
>> HandleError is only called for errors not warnings or
>> informational
>> messages. See the text "The HandleError attribute can be
>> used to provide
>> your own alternative behaviour in case of errors" in the
>> DBI docs.
>>
>> HandleSetError is called whenever the err, errstr or state
>> variables are
>> being set - this includes warnings and informational
>> messages. See the text
>>
>> "The HandleSetErr attribute can be used to intercept the
>> setting of
>> handle err, errstr, and state values." and
>>
>> "The HandleSetErr and HandleError subroutines differ in
>> subtle but
>> significant ways. HandleError is only invoked at the point
>> where the DBI
>> is about to return to the application with err set true.
>> It's not
>> invoked by the failure of a method that's been called by
>> another DBI
>> method. HandleSetErr, on the other hand, is called whenever
>> set_err() is
>> called with a defined err value, even if false. So it's not
>> just for
>> errors, despite the name, but also warn and info states."
>>
>> in the DBI docs.
>>
>> DBD::ODBC like many other DBDs supports (to the degree DBI
>> allows) the
>> reporting of errors, warnings and informational messages.
>> If you call
>> err you can find errors, warnings or informationals as
>> described in the
>> docs - "A driver may return 0 from err() to indicate a
>> warning condition
>> after a method call. Similarly, a driver may return an
>> empty string to
>> indicate a 'success with information' condition. In both
>> these cases the
>> value is false but not undef." As an example:
>>
>> use DBI;
>> use strict;
>> use warnings;
>>
>> my $h = DBI->connect;
>> print "last informational message was: ", $h->errstr,
>> "\n\n"
>>      if $h->err eq "";
>>
>> produces:
>>
>> last informational message was: [unixODBC][Easysoft][SQL
>> Server
>> Driver][SQL Server]Changed language setting to us_english.
>> (SQL-01000)
>> [unixODBC][Easysoft][SQL Server Driver][SQL Server]Changed
>> database
>> context to 'master'. (SQL-01000)
>>
>> in my setup.
>>
>> To my knowledge DBI does not allow multiple informational
>> messages to be
>> stacked i.e., there is only one err, errstr, state and so
>> even if
>> DBD::ODBC calls set_err multiple times you will only see
>> the last
>> informational in err, errstr and state (this is one aspect
>> of this I'm
>> not totally certain about and the above example might
>> illustrate why I
>> could be wrong here). You can catch these informationals
>> with
>> HandleSetError but in the case of DBD::ODBC calling
>> SQLMoreResults
>> automatically, it does not report each informational - I
>> can change that
>> specifically if you like and if you report it on rt.
>>
>> Now some history and operational notes on DBD::ODBC:
>>
>> SQL Server is rather strange when it comes to the calling
>> of procedures.
>> Firstly it does not batch result-sets up and hence
>> odbc_more_results
>> i.e., if you call select 1; select 2; etc in your procedure
>> you have to
>> call odbc_more_results (SQLMoreResults in the ODBC API) to
>> move to the
>> next result-set. Similarly if you call insert; insert; you
>> need to call
>> SQLMoreResults. However, because people using DBD::ODBC
>> said it was a
>> PITA the call odbc_more_results for each insert (as all
>> they'd find out
>> is the RowCount and nothing else unless an error occurs)
>> DBD::ODBC skips
>> non-result-set generating statements automatically i.e., if
>> you do:
>>
>> insert
>> insert
>> select
>> select
>>
>> the first 2 are skipped and the result from the first
>> select is
>> available then you call odbc_more_results to get the next
>> result-set.
>>
>> Also, the SQL Server ODBC driver works differently when
>> calling
>> prepare/execute from calling SQLExcDirect e.g., you can
>> only use
>> temporary tables when using SQLExecDirect. Initially, this
>> was the
>> reason for adding odbc_exec_direct. As it turns out there
>> are other
>> scenarios where odbc_exec_direct is worth using and you
>> have hit one of
>> them. As far as I am aware you need to set odbc_exec_direct
>> to call your
>> procedures reliably - it is something to do with the
>> done_in_proc states
>> returned by TDS.
>>
>> Also, you should note that because SQL Server does not
>> batch statements
>> up in a procedure the output bound parameters are not
>> available until
>> AFTER the procedure is completed and it has not completed
>> until
>> SQLMoreResults returns SQL_NO_DATA - this is the second
>> reason for
>> odbc_more_results.
>>
>> DBD::ODBC does not call SQLMoreResults if you use the do
>> method since do
>> should not be used for result-set generating statements.
>>
>> Now back to what I believe are the issues you have brought
>> up:
>>
>> o you need to use prepare/execute and set odbc_exec_direct
>> on the
>> prepare call.
>>
>> o you do not need to use odbc_more_results in this case if
>> you use
>> prepare/execute since DBD::ODBC will call SQLMoreResults
>> for you
>> internally. N.B. odbc_more_results does not cause
>> SQLMoreResults to be
>> called it simply tells you if when it was called it said
>> there were more
>> results.
>>
>> I am now unsure if you are still saying your restore does
>> not work but
>> the following code works for me:
>>
>> use DBI;
>> use strict;
>> use warnings;
>> use Data::Dumper;
>>
>> sub _error_handler {
>>      print "ERROR HANDLER ", Dumper(\@_);
>>      0;
>> }
>>
>> sub _seterr_handler {
>>        print "SETERR HANDLER ",
>> Dumper(\@_);
>>        0;
>> }
>>
>> my $h = DBI->connect;
>> print "Connection SUCCESS_WITH_INFO data", $h->errstr,
>> "\n\n"
>>      if $h->err eq "";
>>
>> $h->{RaiseError} = 1;
>> $h->{PrintError} = 1;
>> $h->{PrintWarn} = 1;
>> $h->{HandleError} = \&_error_handler;
>> $h->{HandleSetErr} = \&_seterr_handler;
>>
>> #eval {$h->do('create database foo');};
>>
>> my $s = $h->prepare(q{backup database foo to
>> disk='c:\foo.one'}
>> ,{odbc_exec_direct =>  1}
>> );
>> $s->execute;
>> print "errstr: ", $s->errstr, "\n" if $s->err eq "";
>>
>> print "backed up\n";
>> #$h->do(q{backup database foo to disk='c:\foo.bak'});
>>
>> $h->disconnect;
>>
>> print "Restoring\n";
>> $h = DBI->connect;
>> print "Connection SUCCESS_WITH_INFO data", $h->errstr,
>> "\n\n"
>>      if $h->err eq "";
>>
>> $h->{RaiseError} = 1;
>> $h->{PrintError} = 1;
>> $h->{PrintWarn} = 1;
>> $h->{HandleError} = \&_error_handler;
>>
>> $s = $h->prepare(q{restore database foo from
>> disk='c:\foo.one'}
>> ,{odbc_exec_direct =>  1});
>> $s->execute;
>> print "errstr: ", $s->errstr, "\n" if $s->err eq "";
>>
>> and produces:
>>
>> C:\perlbuild_xxx>perl backup_restore.pl
>> Connection SUCCESS_WITH_INFO data[Microsoft][ODBC SQL
>> Server Driver][SQL
>> Server]
>> Changed database context to 'master'. (SQL-01000)
>> [Microsoft][ODBC SQL Server Driver][SQL Server]Changed
>> language setting
>> to us_english. (SQL-01000)
>>
>> SETERR HANDLER $VAR1 = [
>>            bless( {}, 'DBI::st' ),
>>            '',
>>            '[Microsoft][ODBC SQL
>> Server Driver][SQL Server]Processed 176
>> pages for database \'foo\', file \'foo\' on file 1.
>> (SQL-01000)',
>>            '01000',
>>            undef
>>          ];
>> errstr: [Microsoft][ODBC SQL Server Driver][SQL
>> Server]Processed 176
>> pages for database 'foo', file 'foo' on file 1.
>> (SQL-01000)
>> backed up
>> Restoring
>> Connection SUCCESS_WITH_INFO data[Microsoft][ODBC SQL
>> Server Driver][SQL
>> Server]
>> Changed database context to 'master'. (SQL-01000)
>> [Microsoft][ODBC SQL Server Driver][SQL Server]Changed
>> language setting
>> to us_english. (SQL-01000)
>>
>> errstr: [Microsoft][ODBC SQL Server Driver][SQL
>> Server]Processed 176
>> pages for database 'foo', file 'foo' on file 1.
>> (SQL-01000)
>>
>> If I look at the ODBC trace I see it actually gets the
>> following
>> informationals:
>>
>> 01000 Processed 176 pages for database 'foo' file foo
>> 01000 Processed 1 pages for database foo, file foo_log
>> 01000 BACKUP DATABASE successfully processed 177 pages in
>> 0.735 seconds
>> (1972 MS/sec)
>>
>> Now if all you want is to capture all those informationals
>> in
>> HandleSetError then report it on rt and I'll try and change
>> it for you.
>>
>> Martin
>> -- 
>> Martin J. Evans
>> Easysoft Limited
>> http://www.easysoft.com
>>      
>
>
> I apologize if it seems my point is wandering or if I am genuinely unhappy.  That's not my intention, to be sure.
>
> Yes, it does work for me when {odbc_exec_direct} is set to true.  I also understand your explanation of why this is the correct way to do things.
>
>    
You need to set odbc_exec_direct - sorry if this prove inconvenient for 
you but there is no other way for DBD::ODBC to know you need to do this.

> The real concern here is why error 3224 seems untrappable if this restore is done with {odbc_exec_direct} is set to false.  I cannot see how to trap this error at all.
>
>    
I don't think there is a way to do this. I replicated your situation in 
C via ODBC and get the same issue. You could perhaps talk to Microsoft 
but I doubt it will get you anywhere.

> Is it possible to (incorrectly) set odbc_exec_direct to false, issue the database restore, and trap error 3224 in perl?
>
>    
I don't believe so via Perl and DBD::ODBC or even directly via the ODBC API.

> Plausible use cases for why this is a concern: 1) A person wants to use prepared statements as much as possible; it's not clear without extensive server side tracing which may be subject to this problem.   2) Add to case #1 a large shop where a number of people may be writing code.  It would be best to have a sure way to trap any possible server-side error, even if they did something incorrect such as setting odbc_exec_direct to false for a database restore.
>
> Last, thanks for the suggestion about filing an rt ticket re: capturing all informational messages.  I think I will take you up on this, and I'll try and make a case for the value of this in the rt ticket.
>
>    
To be honest, I've already made the changes in DBD::ODBC and if you mail 
me privately I'll send you a new distribution.

> Thank you once again for your time on this.  I do really appreciate it.
>
> Regards,
> -rt
>    

In fairness, perhaps due to my inability to follow where you were going 
I've taken a bit too long to get to the gist of your issues and it has 
arguably highlighted an issue in SQLMoreResults not calling set_err in 
DBI. I always try to help although not always successfully.

Martin
--
Martin J. Evans

Easysoft Limited
http://www.easysoft.com
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.