[freetds] Server - Problems with TDS 4.2 COLFMT token

James K. Lowden jklowden at schemamania.org
Fri May 14 00:42:07 EDT 2004


On Thu, 13 May 2004, Steve Kirkendall <skirkendall at dsl-only.net> wrote:
> 
> Most of my success in writing a tiny SQL server has come with TDS 5.0.
> Now I'm making another attempt at using TDS 4.2, and I'm running into a
> problem.  It seems that the client (the FreeTDS ODBC driver on a Windows
> machine) is parsing the 0xA1(COLFMT) token incorrectly.  It keeps
> reading column info past the end of the token.

OK, first you send 0xA0, from which the client derives the number of
columns.  That looks fine, and, you're right, the information is not
repeated in the next packet.  

Then you want to send 0xA1.  The best reference I know is
src/tds/token.c::tds_process_col_fmt().  Based on that, for N columns the
packet consists of N frames of:

bytes	type	name
-----	----	--------------
2	int-16	usertype
2	mask	flags
	[Sybase uses a 4-byte usertype, no flags]
1	int-8	column_type
0/1/4	int	column_size 

[If column_type requires a 4-byte width descriptor, that implies the
following field is the length of the table name (!)]
n	char[n]	table name, n == characters in table name. 

[If column_size is a 1-byte field, it's the width of the column, for
variable-size and nullable datatypes.]
1	int-8	column_size

sp_datatype_info normally has these metadata ('-' indicates it's missing
from your 0xA0 packet, which you presumably intend):

col     name                type      size    varies  
------  ------------------  --------  ------  ------  
     1  	 TYPE_NAME  	char	 255	   1  
     2  	 DATA_TYPE  smallint	   2	   0  
     3  	 PRECISION	 int	   4	   1  
     4      LITERAL_PREFIX  	char	  32	   1  
     5      LITERAL_SUFFIX  	char	  32	   1  
     6       CREATE_PARAMS  	char	  32	   1  
     7  	  NULLABLE  smallint	   2	   1  
     8      CASE_SENSITIVE  smallint	   2	   0  
     9  	SEARCHABLE  smallint	   2	   0  
    10  UNSIGNED_ATTRIBUTE  smallint	   2	   1  
    11  	     MONEY  smallint	   2	   0  
    12      AUTO_INCREMENT  smallint	   2	   1  
    13     LOCAL_TYPE_NAME  	char	 255	   1  
-   14       MINIMUM_SCALE  smallint	   2	   1  
-   15       MAXIMUM_SCALE  smallint	   2	   1  
-   16       SQL_DATA_TYPE  smallint	   2	   0  
-   17    SQL_DATETIME_SUB  smallint	   2	   1  
-   18      NUM_PREC_RADIX	 int	   4	   1  
-   19  INTERVAL_PRECISION  smallint	   2	   1  
-   20  	  USERTYPE  smallint	   2	   1  

So we should expect 13 columns in your 0xA1.  (In your 0xA0, "PRECISION"
is unnamed.)  After that, the client would expect 0xD1 (row data).  And,
indeed, that's what your're sending.  I parse your packet thus:

a1 46 00 
	00 00 00 00 	2f 	20 
	00 00 00 00 	34
	00 00 00 00 	38 	
	00 00 00 00 	2f 	20 
	00 00 00 00 	2f 	20 
	00 00 00 00 	2f 	00 
	00 00 00 00 	34 
	00 00 00 00 	34 
	00 00 00 00 	34 
	00 00 00 00 	34 
	00 00 00 00 	34 
	00 00 00 00 	34 
	00 00 00 00 	2f 	20 
	
ae 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 00 
^^
This is the next token.  

d1 04 43 48 41 52 01 00 00 00 00 00 01 27 01 27 00 00 00 00 00 [...]
^^
Here's the start of the row data.  

By way of comparison, here's the same answer from a 7.0 server:

a1 73 00 
	02 00 01 00 	27 	ff 
	06 00 08 00 	34
	0d 00 01 00 	26 	04 
	02 00 09 00 	27 	20 
	02 00 09 00 	27 	20 
	02 00 09 00 	27 	20 
	0d 00 01 00 	26 	02 
	06 00 08 00 	34 
	06 00 08 00 	34 
	0d 00 09 00 	26 	02 
	06 00 08 00 	34 
	0d 00 09 00 	26 	02 
	02 00 01 00 	27 	ff 
	0d 00 01 00 	26 	02 
	0d 00 01 00 	26 	02 
	06 00 08 00 	34 
	0d 00 09 00 	26 	02 
	0d 00 01 00 	26 	04 
	0d 00 01 00 	26 	02 
	0d 00 01 00 	26 	02 

a9 	04 00 	02 0c 0b 00

I don't know why, but tds_process_col_fmt() appears not to recognize when
it has read 13 columns' worth of data.  It is as though it loses track of
how many there are.  It sees a "14th" column:
	
	ae 0d 00 00 	00 	00 
	00 00 00 00 ...

and claims to find a type = 0.  Really, it should just punt at that point.
 

If I were you, I'd get out gdb or put in some logging statements.  I'd
suspect memory corruption of some kind, but I really can't guess where.  

> Also, the description of the COLFMT token in "tds.html" seems to be
> way off.  It shows 0xA1 as containing column names (duplicating the
> names sent via 0xA0(COLNAME) tokens) but really it just contains four
> NUL bytes, a type byte and optionally a length byte for each column...
> assuming the tds_send_colinfo() function in src/server/server.c is
> correct.

Thanks for pointing that out.  It's been a looong time since I looked at
the 4.2 packets.  

--jkl



More information about the FreeTDS mailing list