[freetds] ODBC handling of raiserror

ZIGLIO, Frediano, VF-IT Frediano.Ziglio at vodafone.com
Mon Feb 12 07:07:05 EST 2007


> 
> James K. Lowden wrote:
> > IMO SQLExecute should
> > return SQL_ERROR only when it *failed* e.g. couldn't send 
> the SQL to the
> > server or the server could not execute the SQL.  If we get 
> a TDS_ERROR
> > packet followed by a DONE packet, SQLExecute should fail.  
> Otherwise, it
> > should return WITH_INFO.  
> 
> I am concerned that our ODBC result processing is weak.  I 
> think part of
> the reason it's weak is that resultset processing is 
> implemented in the
> client libraries.  Consider:
> 
> 1.  ct-lib, db-lib, and ODBC all do fundamentally the same thing.  
> 2.  Each one uses libtds, each in its own way.  
> 3.  It would be possible to write any one in terms of the 
> other.  E.g., it
> would be possible to write an ODBC driver that uses db-lib instead of
> libtds.  
> 

I don't think we haven't a full knowledge on how every library should
work... try to check output of done_handling using our library and
proprietary ones.
Consider that ODBC 2 and ODBC 3 behave like different world... and they
are only 2 version of the same library...
It's so complicated that when I don't understand what to do I just try
to reproduce ms odbc. At least we can say that what is working using ms
proprietary library works for our library. Even ms documentation is not
coherent... for instance in some cases they state that a recordset must
have some columns while other pages state that some statements can have
no columns. For experience odbc 2 must have columns, in odbc 3 this is
not a must, for instance a RAISERROR or PRINT statement produce usually
a different statement. Usually cause SET ROWCOUNT setting can change
this behaviour.

> I think we should define an API for libtds that can be used 
> by both ODBC
> and db-lib.  I think they share functionality that could 
> usefully be in
> libtds.  I would like feedback on the idea.  
> 
> Consider this table:
> 
> db-lib		ODBC		libtds (proposed)
> --------------  --------------  -----------------
> dbexecsql	SQLExecute	tds_execsql
> dbresults	SQLMoreResults	tds_results
> dbnextrow	SQLFetch	tds_nextrow
> 
> The trickiest part, I think we all agree, is DONE handling, 
> which would be
> in tds_nextrow.  A TDS_ROW packet may be followed by:
> 
> 1.  Another TDS_ROW (easy), or
> 2.  TDS_DONE (also easy), or
> 3.  TDS_ERROR / TDS_EED, or 
> 4.  Any number of compute rows, output parameters, and return 
> status.  
> 

Yes, I noted however that some library in some cases read DONE and
PARAMS "together" stopping at next ERROR/EED or at ROWFMT, in other
cases they stop at DONE. One way to note this behavior is to check if
libraries store output parameters or store result.

> (Normally, any errors about a query are returned before the data, but
> sometimes the error occurs within a row e.g. "select type, 
> type/(type-108)
> from systypes where type > 100".  After encountering such an 
> error, the
> server stops sending rows for that query.)  
> 

mm... error.c test check this case. I reminds that sybase stops while
mssql return the error and then continue with next row.

> Currently, there is too much stream handling -- too much 
> awareness of what
> tokens have been received or are expected -- in the client 
> libraries, and
> too much state information is shared between them and libtds. 
>  db-lib and
> ODBC have *very* different ideas about buffering and cursors, but they
> both need to process *exactly* the same packets.  Let's do the packet
> processing (stream handling) in libtds, once and for all.  
> 
> Consider just these lines from dbnextrow and SQLFetch:
> 
> db-lib:
> 	const int mask =
> TDS_STOPAT_ROWFMT|TDS_RETURN_DONE|TDS_RETURN_ROW|TDS_RETURN_COMPUTE;
> 	...
> 	switch (tds_process_tokens(tds, &res_type, NULL, mask)) {
> 
> ODBC:
> 	odbc_process_tokens(stmt,
> TDS_RETURN_ROW|TDS_STOPAT_COMPUTE|TDS_STOPAT_ROW);
> 
> None of this is necessary or desirable!  
> 
> tds_nextrow would read a TDS_ROW packet.  It would peek at 
> the stream, and
> keep processing it until it saw a DONE packet.  For "after regular
> results" data, it would populate a structure that the client 
> library could
> query at its leisure.  
> 
> My goal is to remove every call to tds_process_tokens from the client
> libraries.  Eventually, the client libraries would call only 
> a few libtds
> functions, all defined in src/tds/api.c.  There would be less state
> information to share.  The read-until-mask logic would go away.  The
> "what's next" states would be simplified to 1) row pending, 2) results
> pending, or 3) nothing pending.  To get "post results" data 
> (e.g. return
> status), the client library wouldn't even examine the stream; it would
> examine its own data structure to see if it was populated by 
> tds_nextrow. 
> 

So, stop at the "minimun token" (that is process how many tokens as the
library which would process less tokens) encountered and let the library
continue (if necessary). It sounds resonable. Mmmm... sometime I think
that message shouldn't call a handler but returns the error. 

> 
> I consider this to be a logical next step for libtds.  Some 
> time ago we
> removed all the endianism from the client libraries and made sure that
> issue was completely handled in libtds.  More recently 
> Frediano moved all
> network calls to net.c.  I propose to move all the stream-state
> information and all knowledge of token sequences into libtds. 
>  All that
> would remain in the client libraries would be error checking and
> "personalities".  Plenty enough, those.  
> 
> What do you think?  
> 

I like the idea of having a "next token" information. I don't like that
dblib have to call tds_peek directly. 

freddy77




More information about the FreeTDS mailing list