More on using FreeTDS on a server (was Re: [freetds] Server dumps core...)

Steve Kirkendall skirkendall at
Sat Apr 17 21:38:01 EDT 2004

Here's the latest on my efforts to use FreeTDS for server-side networking.

First the bad news: Microsoft's ODBC driver for SQL Server still refuses
to talk to my little server.  All I get is a generic "Protocol error in
TDS stream".  I suspect I'm failing to send some critical token in the
login response, but it's hard to be sure with an error message that vague.

Now a little the good news: I can now use the tsql program to submit
queries, and get tables back.  The tables don't look quite right --
strings come across okay, but numbers are mangled.  But I'm definitely
making progress.

And the real purpose of this message: Questions!

* Can anybody recommend an alternative ODBC/TDS driver for Windows?  One
  that, maybe, would give better error messages?  Or it would also be okay
  if it simply worked, I suppose.

* I have a log, produced by FreeTDS, of the network traffic in my failed
  attempts to access my little SQL server via Microsoft's ODBC driver.
  Does anybody out there have a similar dump of a *successful* login?
  Nothing fancy, just a TDS4.2 login such as the "test connection"
  button in the ODBC Microsoft SQL Server Setup dialog.

* What should I do about queries that don't result in tables?  For example,
  tsql sends a "set textsize 64512" request.  My server currently just
  sends token #253 (Result Set Done).  Is that proper?

* Are there any common "gotchas" when sending numbers?  It kind of looks
  like tsql is printing the addresses of the numbers, instead of the
  numbers themselves -- the values are all similar, but never identical.
  I don't think it's an endian thing.

* Can anybody spot any obvious problems with my table-sending function,
  below?  The XXXX_t names are all types; their purpose should be pretty
  obvious, except that rows are represented as an array of value_t values.
  value_t is a union of all supported data types.  For strings, it is a
  (char *) and the actual text of the string is appended to each row's 
  value_t array; that way, when the row is freed so are all of its strings.
  Column types, names, etc. are stored in the table->col array.  By looking
  at the log file, I have verified that my little SQL server is indeed
  sending garbage for integers; this isn't a bug in tsql.

/* send a table to the client */
void tdsprinttable(table)
	table_t table;	/* the table to send */
	TDSRESULTINFO	*resinfo;/* FreeTDS library version of table */
	int		r, c;	/* row & column counters */
	int		rowsize;/* maximum row memory needed */
	int		offset;	/* offset of field within row */

	/* allocate a FreeTDS table */
	resinfo = tds_alloc_results(table->ncols);

	/* fill in the column info for all columns */
	rowsize = 0;
	for (c = 0; c < table->ncols; c++)
		/* fill in this column's info */
		switch (table->col[c].type)
		  case OP_STRING:
			resinfo->columns[c]->column_type = SYBCHAR;
			resinfo->columns[c]->column_size = table->col[c].width;
		  case OP_INTEGER:
			resinfo->columns[c]->column_type = SYBINT4;
			resinfo->columns[c]->column_size = sizeof(long);

		  case OP_NUMERIC:
			resinfo->columns[c]->column_type = SYBFLT8;
			resinfo->columns[c]->column_size = sizeof(double);

		  case OP_BOOLEAN:
			resinfo->columns[c]->column_type = SYBBIT;
			resinfo->columns[c]->column_size = 1;
		resinfo->columns[c]->column_namelen =

		/* for non-string data, the data offsets don't change so
		 * we might as well set them here.
		resinfo->columns[c]->column_offset = c * sizeof(value_t);

		/* add this column's row data size to rowmax */
		rowsize += resinfo->columns[c]->column_size;

	/* start the result, and send column info */
	tds_send_result(tds, resinfo);
	tds_send_174_token(tds, table->ncols);

	/* for each row... */
	for (r = 0; r < table->nrows; r++)
		/* tell FreeTDS where the data is */
		resinfo->current_row = (unsigned char *)&table->row[r];

		/* convert the row */
		for (c = 0; c < table->ncols; c++)
			/* most fields' offsets don't change, but we do
			 * need to worry about strings.
			if (table->col[c].type == OP_STRING)
				resinfo->columns[c]->column_offset =
					(unsigned char *)table->row[r][c].string
						- resinfo->current_row;

		/* send the row */
		tds_send_row(tds, resinfo);
	resinfo->current_row = NULL;

	/* end the table */
	tds_send_253_token(tds, 16, table->nrows);

	/* clean up */
	didresult = 1;

Steve Kirkendall    |A:It is confusing, since people don't read that way
kirkenda at |Q:Why is top-posting bad?
                    |A:It is adding comments to the top of a message
                    |Q:What is top-posting?

More information about the FreeTDS mailing list