From ce71271605955feab8b9478fb2f121e379b49d41 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Henrik=20Grubbstr=C3=B6m=20=28Grubba=29?=
 <grubba@grubba.org>
Date: Fri, 10 Feb 2006 15:45:56 +0100
Subject: [PATCH] Improved buffering. Should now avoid reading past packet
 borders.

Rev: lib/modules/Sql.pmod/tds.pike:1.6
---
 lib/modules/Sql.pmod/tds.pike | 411 ++++++++++++++++++----------------
 1 file changed, 214 insertions(+), 197 deletions(-)

diff --git a/lib/modules/Sql.pmod/tds.pike b/lib/modules/Sql.pmod/tds.pike
index 919a947ba4..78d3bc0b1a 100644
--- a/lib/modules/Sql.pmod/tds.pike
+++ b/lib/modules/Sql.pmod/tds.pike
@@ -1,5 +1,5 @@
 /*
- * $Id: tds.pike,v 1.5 2006/02/10 13:08:43 grubba Exp $
+ * $Id: tds.pike,v 1.6 2006/02/10 14:45:56 grubba Exp $
  *
  * A Pike implementation of the TDS protocol.
  *
@@ -217,100 +217,116 @@ static {
 #define FMT_INT		"%-4c"
 #define FMT_INT8	"%-4c"
 
-    int inpos = 0;
-    string inbuf = "";
-    static void fill_buf()
+    class InPacket
     {
-      if (!busy) {
-	TDS_WERROR("Filling buffer on idle connection!\n");
-      }
+      int inpos = 0;
+      string inbuf = "";
+      int done;
 
-      string header = socket->read(8);
-      if (!header || sizeof(header) < 8) {
-	tds_error("Failed to read packet header: %O, %s.\n",
-		  header, strerror(socket->errno()));
-      }
-      TDS_WERROR("Read header:\n%s\n", hex_dump(header));
-      int packet_type;
-      int last_packet;
-      int len;
-      // NOTE: Network byteorder!!
-      sscanf(header, "%-1c%-1c%2c", packet_type, last_packet, len);
-      len -= 8;
+      static void fill_buf()
+      {
+	if (done) {
+	  TDS_WERROR("Filling buffer on finished packet!\n"
+		     "%s\n", hex_dump(inbuf));
+	}
 
-      busy = !last_packet;
+	string header = socket->read(8);
+	if (!header || sizeof(header) < 8) {
+	  busy = !(done = 1);
+	  tds_error("Failed to read packet header: %O, %s.\n",
+		    header, strerror(socket->errno()));
+	}
+	TDS_WERROR("Read header:\n%s\n", hex_dump(header));
+	int packet_type;
+	int last_packet;
+	int len;
+	// NOTE: Network byteorder!!
+	sscanf(header, "%-1c%-1c%2c", packet_type, last_packet, len);
+	len -= 8;
+
+	busy = !(done = last_packet);
+
+	string data = socket->read(len);
+	if (!data || sizeof(data) < len) {
+	  tds_error("Failed to read packet data (%d bytes), got %O (%d bytes), %s\n",
+		    len, data, sizeof(data||""), strerror(socket->errno()));
+	}
+	TDS_WERROR("Read packet with %d bytes.\n%s\n",
+		   sizeof(data), hex_dump(data));
+	inbuf = inbuf[inpos..] + data;
+	inpos = 0;
+      }
 
-      string data = socket->read(len);
-      if (!data || sizeof(data) < len) {
-	tds_error("Failed to read packet data (%d bytes), got %O (%d bytes), %s\n",
-		  len, data, sizeof(data||""), strerror(socket->errno()));
+      string get_raw(int bytes)
+      {
+	while (inpos + bytes > sizeof(inbuf)) {
+	  fill_buf();
+	}
+	string raw = inbuf[inpos..inpos + bytes - 1];
+	inpos += bytes;
+	return raw;
       }
-      TDS_WERROR("Read packet with %d bytes.\n%s\n",
-		 sizeof(data), hex_dump(data));
-      inbuf = inbuf[inpos..] + data;
-      inpos = 0;
-    }
 
-    string get_raw(int bytes)
-    {
-      while (inpos + bytes > sizeof(inbuf)) {
-	fill_buf();
+      string peek_raw(int bytes)
+      {
+	while (inpos + bytes > sizeof(inbuf)) {
+	  fill_buf();
+	}
+	string raw = inbuf[inpos..inpos + bytes - 1];
+	return raw;
       }
-      string raw = inbuf[inpos..inpos + bytes - 1];
-      inpos += bytes;
-      return raw;
-    }
 
-    string peek_raw(int bytes)
-    {
-      while (inpos + bytes > sizeof(inbuf)) {
-	fill_buf();
+      int get_int8()
+      {
+	return array_sscanf(get_raw(8), "%-8c")[0];
+      }
+      int get_int()
+      {
+	return array_sscanf(get_raw(4), "%-4c")[0];
+      }
+      int get_int_be()
+      {
+	return array_sscanf(get_raw(4), "%4c")[0];
+      }
+      int get_smallint()
+      {
+	return array_sscanf(get_raw(2), "%-2c")[0];
+      }
+      int get_byte()
+      {
+	return array_sscanf(get_raw(1), "%-1c")[0];
+      }
+      int peek_byte()
+      {
+	return array_sscanf(peek_raw(1), "%-1c")[0];
       }
-      string raw = inbuf[inpos..inpos + bytes - 1];
-      return raw;
-    }
 
-    int get_int8()
-    {
-      return array_sscanf(get_raw(8), "%-8c")[0];
-    }
-    int get_int()
-    {
-      return array_sscanf(get_raw(4), "%-4c")[0];
-    }
-    int get_int_be()
-    {
-      return array_sscanf(get_raw(4), "%4c")[0];
-    }
-    int get_smallint()
-    {
-      return array_sscanf(get_raw(2), "%-2c")[0];
-    }
-    int get_byte()
-    {
-      return array_sscanf(get_raw(1), "%-1c")[0];
-    }
-    int peek_byte()
-    {
-      return array_sscanf(peek_raw(1), "%-1c")[0];
-    }
+      string get_string(int len)
+      {
+	if (!len) return "";
+	TDS_WERROR("get_string(%d)...\n", len);
+	return utf16_to_string(get_raw(len*2));
+      }
 
-    string get_string(int len)
-    {
-      if (!len) return "";
-      TDS_WERROR("get_string(%d)...\n", len);
-      return utf16_to_string(get_raw(len*2));
-    }
+      void expect(string s)
+      {
+	string r = get_raw(sizeof(s));
+	if (r != s) {
+	  tds_error("Expectation failed: Got %O, expected %O\n",
+		    r, s);
+	}
+      }
 
-    void expect(string s)
-    {
-      string r = get_raw(sizeof(s));
-      if (r != s) {
-	tds_error("Expectation failed: Got %O, expected %O\n",
-		  r, s);
+      static void create()
+      {
+	if (!busy) {
+	  TDS_WERROR("Creating input packet on idle connection!\n");
+	}
       }
     }
 
+    //static InPacket login_answer;
+
     class Packet
     {
       array(string|int) segments = ({});
@@ -427,14 +443,6 @@ static {
       if (busy) {
 	TDS_WERROR("Sending packet on busy connection!\n");
       }
-      if (sizeof(inbuf) > inpos) {
-	TDS_WERROR("inbuf: %O\n"
-		   "%s\n",
-		   inbuf[inpos..],
-		   hex_dump(inbuf[inpos..]));
-      }
-      inbuf = "";
-      inpos = 0;
       busy = last;
 
       // FIXME: Split large packets!
@@ -451,7 +459,7 @@ static {
 	tds_error("Failed to send packet.\n"
 		  "raw: %O\n", raw);
       }
-      Stdio.write_file("packet.bin", raw);
+      //Stdio.write_file("packet.bin", raw);
     }
 
     static string crypt_pass(string password)
@@ -464,7 +472,7 @@ static {
 			 });
     }
 
-    static void send_login()
+    static InPacket send_login()
     {
       password = password[..127];
 
@@ -508,6 +516,7 @@ static {
       
       TDS_WERROR("Sending login packet.\n");
       send_packet(p, 0x10, 1);
+      return InPacket();
     }
 
     string ecb_encrypt(string data, string key)
@@ -571,29 +580,29 @@ static {
       send_packet(p, 0x11);
     }
 
-    static void process_auth()
+    static void process_auth(InPacket inp)
     {
-      int pdu_size = get_smallint();
+      int pdu_size = inp->get_smallint();
       if (pdu_size < 32) tds_error("Bad pdu size: %d\n", pdu_size);
-      expect("NTLMSSP\0");
-      get_int();	/* sequence -> 2 */
-      get_int();	/* domain len * 2 */
-      get_int();	/* domain offset */
-      get_int();	/* flags */
-      string nonce = get_raw(8);
+      inp->expect("NTLMSSP\0");
+      inp->get_int();	/* sequence -> 2 */
+      inp->get_int();	/* domain len * 2 */
+      inp->get_int();	/* domain offset */
+      inp->get_int();	/* flags */
+      string nonce = inp->get_raw(8);
       /* Discard context, target and data info */
-      get_raw(pdu_size - 32);
+      inp->get_raw(pdu_size - 32);
       TDS_WERROR("Got nonce: %O\n", nonce);
       send_auth(nonce);
     }
 
-    static void process_msg(int token_type)
+    static void process_msg(InPacket inp, int token_type)
     {
       TDS_WERROR("TDS_ERROR_TOKEN | TDS_INFO_TOKEN | TDS_EED_TOKEN\n");
-      int len = get_smallint();
-      int no = get_int();
-      int state = get_byte();
-      int level = get_byte();
+      int len = inp->get_smallint();
+      int no = inp->get_int();
+      int state = inp->get_byte();
+      int level = inp->get_byte();
       int is_error = 0;
       int has_eed = 0;
       switch(token_type) {
@@ -601,11 +610,11 @@ static {
 	TDS_WERROR("TDS_EED_TOKEN\n");
 	if (level > 10) is_error = 1;
 
-	int state_len = get_byte();
-	string state = get_raw(state_len);
-	has_eed = get_byte();
+	int state_len = inp->get_byte();
+	string state = inp->get_raw(state_len);
+	has_eed = inp->get_byte();
 	/* Ignore status and transaction state */
-	get_smallint();
+	inp->get_smallint();
 	break;
       case TDS_INFO_TOKEN:
 	TDS_WERROR("TDS_INFO_TOKEN\n");
@@ -615,17 +624,17 @@ static {
 	is_error = 1;
 	break;
       }
-      string message = get_string(get_smallint());
-      string server = get_string(get_byte());
-      string proc_name = get_string(get_byte());
-      int line = get_smallint();
+      string message = inp->get_string(inp->get_smallint());
+      string server = inp->get_string(inp->get_byte());
+      string proc_name = inp->get_string(inp->get_byte());
+      int line = inp->get_smallint();
       if (has_eed) {
 	while (1) {
-	  switch(peek_byte()) {
+	  switch(inp->peek_byte()) {
 	  case TDS5_PARAMFMT_TOKEN:
 	  case TDS5_PARAMFMT2_TOKEN:
 	  case TDS5_PARAMS_TOKEN:
-	    process_default_tokens(get_byte());
+	    process_default_tokens(inp, inp->get_byte());
 	    continue;
 	  }
 	  break;
@@ -640,13 +649,13 @@ static {
       }
     }
 
-    static void process_env_chg()
+    static void process_env_chg(InPacket inp)
     {
-      int size = get_smallint();
-      int env_type = get_byte();
+      int size = inp->get_smallint();
+      int env_type = inp->get_byte();
       if (env_type == TDS_ENV_SQLCOLLATION) {
-	size = get_byte();
-	string block = get_raw(size);
+	size = inp->get_byte();
+	string block = inp->get_raw(size);
 	if (size >= 5) {
 	  string collation = block[..4];
 	  int lcid;
@@ -654,11 +663,11 @@ static {
 	  /* FIXME: Should we care? */
 	}
 	/* Discard old collation. */
-	get_raw(get_byte());
+	inp->get_raw(inp->get_byte());
 	return;
       }
-      string new_val = get_string(get_byte());
-      string old_val = get_string(get_byte());
+      string new_val = inp->get_string(inp->get_byte());
+      string old_val = inp->get_string(inp->get_byte());
       switch(env_type) {
       case TDS_ENV_PACKSIZE:
 	int new_block_size = (int)new_val;
@@ -798,12 +807,12 @@ static {
       return (col_type == SYBNUMERIC) || (col_type == SYBDECIMAL);
     }
 
-    mapping(string:mixed) tds7_get_data_info()
+    mapping(string:mixed) tds7_get_data_info(InPacket inp)
     {
       mapping(string:mixed) res = ([
-	"usertype":get_smallint(),
-	"flags":get_smallint(),
-	"column_type":get_byte(),
+	"usertype":inp->get_smallint(),
+	"flags":inp->get_smallint(),
+	"column_type":inp->get_byte(),
       ]);
       res->nullable == res->flags & 0x01;
       res->writeable == !!(res->flags & 0x08);
@@ -817,13 +826,13 @@ static {
 	res->column_size = get_size_by_type(res->column_type);
 	break;
       case 4:
-	res->column_size = get_int();
+	res->column_size = inp->get_int();
 	break;
       case 2:
-	res->column_size = get_smallint();
+	res->column_size = inp->get_smallint();
 	break;
       case 1:
-	res->column_size = get_byte();
+	res->column_size = inp->get_byte();
 	break;
       }
       TDS_WERROR("Column size: %d bytes\n", res->column_size);
@@ -833,27 +842,27 @@ static {
 
       if (is_numeric_type(res->cardinal_type)) {
 	TDS_WERROR("is_numeric\n");
-	res->column_prec = get_byte();
-	res->column_scale = get_byte();
+	res->column_prec = inp->get_byte();
+	res->column_scale = inp->get_byte();
       }
       if (is_collate_type(res->column_type)) {
 	TDS_WERROR("is_collate\n");
-	get_raw(4);	// Collation
-	get_byte();	// charset
+	inp->get_raw(4);	// Collation
+	inp->get_byte();	// charset
       }
       if (is_blob_type(res->cardinal_type)) {
 	TDS_WERROR("is_blob\n");
-	res->table = get_string(get_smallint());
+	res->table = inp->get_string(inp->get_smallint());
 	TDS_WERROR("Table name: %O\n", res->table);
       }
-      res->name = get_string(get_byte());
+      res->name = inp->get_string(inp->get_byte());
       TDS_WERROR("Column info: %O\n", res);
       return res;
     }
 
-    static void tds7_process_result()
+    static void tds7_process_result(InPacket inp)
     {
-      int num_cols = get_smallint();
+      int num_cols = inp->get_smallint();
       if (num_cols == 0xffff) {
 	TDS_WERROR("No meta data.\n");
 	column_info = 0;
@@ -864,7 +873,7 @@ static {
       //busy = 1;
       int offset = 0;
       foreach(column_info; int col; ) {
-	column_info[col] = tds7_get_data_info();
+	column_info[col] = tds7_get_data_info(inp);
 	column_info[col]->column_offset = offset;
 	TDS_WERROR("Offset: 0x%04x\n", offset);
 	if (is_numeric_type(column_info[col]->cardinal_type)) {
@@ -880,7 +889,7 @@ static {
       
     }
 
-    static void process_default_tokens(int token_type)
+    static void process_default_tokens(InPacket inp, int token_type)
     {
       if (token_type == TDS_DONE_TOKEN) return;
       switch(token_type) {
@@ -889,13 +898,13 @@ static {
       case TDS_ERROR_TOKEN:
       case TDS_INFO_TOKEN:
       case TDS_EED_TOKEN:
-	process_msg(token_type);
+	process_msg(inp, token_type);
 	return;
       case TDS_ENVCHANGE_TOKEN:
-	process_env_chg();
+	process_env_chg(inp);
 	return;
       case TDS7_RESULT_TOKEN:
-	tds7_process_result();
+	tds7_process_result(inp);
 	break;
       case TDS5_DYNAMIC_TOKEN:
       case TDS_LOGINACK_TOKEN:
@@ -903,7 +912,7 @@ static {
       case TDS_CONTROL_TOKEN:
       case TDS_TABNAME_TOKEN:
 	TDS_WERROR("Skipping token: %d\n", token_type);
-	get_raw(get_smallint());
+	inp->get_raw(inp->get_smallint());
 	break;
       default:
 	TDS_WERROR("FIXME: Got unknown token in process_default_tokens: %d\n",
@@ -912,25 +921,25 @@ static {
       }
     }
 
-    int process_result_tokens()
+    int process_result_tokens(InPacket inp)
     {
       if (!busy) {
 	//return TDS_NO_MORE_RESULTS;
       }
       while (1) {
-	int token_type = peek_byte();
+	int token_type = inp->peek_byte();
 	TDS_WERROR("Got result token %d\n", token_type);
 	switch(token_type) {
 	case TDS7_RESULT_TOKEN:
 	  TDS_WERROR("TDS7_RESULT_TOKEN\n");
-	  get_byte();
-	  tds7_process_result();
-	  if (peek_byte() == TDS_TABNAME_TOKEN) {
+	  inp->get_byte();
+	  tds7_process_result(inp);
+	  if (inp->peek_byte() == TDS_TABNAME_TOKEN) {
 	    TDS_WERROR("TDS_TABNAME_TOKEN\n");
-	    process_default_tokens(get_byte());
-	    if (peek_byte() == TDS_COLINFO_TOKEN) {
+	    process_default_tokens(inp, inp->get_byte());
+	    if (inp->peek_byte() == TDS_COLINFO_TOKEN) {
 	      TDS_WERROR("TDS_COLINFO_TOKEN\n");
-	      get_byte();
+	      inp->get_byte();
 	      //process_colinfo();	// FIXME!
 	    }
 	  }
@@ -941,16 +950,19 @@ static {
 	  TDS_WERROR("==> TDS_ROW_RESULT\n");
 	  return TDS_ROW_RESULT;
 	default:
-	  get_byte();
 	  TDS_WERROR("==> FIXME: process_result_tokens\n");
-	  process_default_tokens(token_type);
+	  // FALL_THROUGH
+	case TDS_ORDERBY_TOKEN:
+	  inp->get_byte();
+	  process_default_tokens(inp, token_type);
 	  return 0;		/***** FIXME:::::: *****/
 	  break;
 	}
       }
     }
 
-    static string|int get_data(mapping(string:mixed) info, int col)
+    static string|int get_data(InPacket inp,
+			       mapping(string:mixed) info, int col)
     {
       TDS_WERROR("get_data for column %d info:%O\n", col, info);
       mapping blobinfo;
@@ -961,25 +973,25 @@ static {
       case 4:
 	switch(info->cardinal_type) {
 	case SYBVARIANT:
-	  int sz = get_int();
+	  int sz = inp->get_int();
 	  if (!sz) return 0;	// NULL;
-	  return get_raw(sz);
+	  return inp->get_raw(sz);
 	case SYBLONGBINARY:
-	  colsize = get_int();
+	  colsize = inp->get_int();
 	  break outer;
 	}
-	int len = get_byte();
+	int len = inp->get_byte();
 	if (len == 16) {
 	  blobinfo = ([
-	    "textptr":get_raw(16),
-	    "timestamp":get_raw(8),
+	    "textptr":inp->get_raw(16),
+	    "timestamp":inp->get_raw(8),
 	  ]);
-	  colsize = get_int();
+	  colsize = inp->get_int();
 	  TDS_WERROR("BLOB: size:%d info:%O\n", colsize, blobinfo);
 	}
 	break;
       case 2:
-	colsize = get_smallint();
+	colsize = inp->get_smallint();
 	if (!colsize) {
 	  // Empty string.
 	  return "";
@@ -987,7 +999,7 @@ static {
 	if (colsize == 0xffff) colsize = 0;
 	break;
       case 1:
-	colsize = get_byte();
+	colsize = inp->get_byte();
 	break;
       case 0:
 	colsize = get_size_by_type(info->cardinal_type);
@@ -997,19 +1009,19 @@ static {
       if (!colsize) return 0;	// NULL.
       if (is_numeric_type(info->cardinal_type)) {
 	// NUMERIC
-	string arr = get_raw(colsize);
+	string arr = inp->get_raw(colsize);
 	TDS_WERROR("NUMERIC: %O\n", arr);
 	return arr;	// FIXME
       } else if (is_blob_type(info->cardinal_type)) {
 	// BLOB
-	string raw = get_raw(colsize);
+	string raw = inp->get_raw(colsize);
 	TDS_WERROR("BLOB. colsize:%d, raw: %O\n", colsize, raw);
 	if (is_char_type(info->cardinal_type)) {
 	  return utf16_to_string(raw);
 	}
 	return raw;
       } else {
-	string raw = get_raw(colsize);
+	string raw = inp->get_raw(colsize);
 	TDS_WERROR("Got raw data: %O\ncolumn_size:%d colsize:%d\n%s\n",
 		   raw, info->column_size, colsize, hex_dump(raw));
 	if (is_unicode_type(info->column_type)) {
@@ -1150,21 +1162,22 @@ static {
       return raw;
     }
 
-    static array(string|int) process_row()
+    static array(string|int) process_row(InPacket inp)
     {
       if (!column_info) return 0;
       array(string|int) res = allocate(sizeof(column_info));
       foreach(column_info; int i; mapping(string:mixed) info) {
-	res[i] = convert(get_data(info, i), info);
+	res[i] = convert(get_data(inp, info, i), info);
       }
       return res;
     }
 
-    array(string|int) process_row_tokens(int|void leave_end_token)
+    array(string|int) process_row_tokens(InPacket inp,
+					 int|void leave_end_token)
     {
       //if (!busy) return 0;	// No more rows.
       while (1) {
-	int token_type = peek_byte();
+	int token_type = inp->peek_byte();
 	TDS_WERROR("Process row tokens: Token type: %d\n", token_type);
 	switch(token_type) {
 	case TDS_RESULT_TOKEN:
@@ -1174,29 +1187,29 @@ static {
 	  //busy = 0;
 	  return 0;
 	case TDS_ROW_TOKEN:
-	  get_byte();
-	  return process_row();
+	  inp->get_byte();
+	  return process_row(inp);
 	  break;
 	case TDS_DONE_TOKEN:
 	case TDS_DONEPROC_TOKEN:
 	case TDS_DONEINPROC_TOKEN:
-	  if (!leave_end_token) get_byte();
+	  if (!leave_end_token) inp->get_byte();
 	  //busy = 0;
 	  return 0;
 	default:
-	  get_byte();
-	  process_default_tokens(token_type);
+	  inp->get_byte();
+	  process_default_tokens(inp, token_type);
 	  break;
 	}
       }
     }
 
-    static void process_login_tokens()
+    static void process_login_tokens(InPacket inp)
     {
       int ok = 0;
       int token_type;
       do {
-	token_type = get_byte();
+	token_type = inp->get_byte();
 	TDS_WERROR("Got token: %d\n", token_type);
 	switch(token_type) {
 	case TDS_DONE_TOKEN:
@@ -1204,18 +1217,18 @@ static {
 	  break;
 	case TDS_AUTH_TOKEN:
 	  TDS_WERROR("TDS_AUTH_TOKEN\n");
-	  process_auth();
+	  process_auth(inp);
 	  break;
 	case TDS_LOGINACK_TOKEN:
 	  TDS_WERROR("TDS_LOGINACK_TOKEN\n");
-	  int len = get_smallint();
-	  int ack = get_byte();
-	  int major = get_byte();
-	  int minor = get_byte();
-	  get_smallint();	/* Ignored. */
-	  get_byte();		/* Product name len */
-	  server_product_name = get_string((len-10)/2);
-	  int product_version = get_int_be();
+	  int len = inp->get_smallint();
+	  int ack = inp->get_byte();
+	  int major = inp->get_byte();
+	  int minor = inp->get_byte();
+	  inp->get_smallint();	/* Ignored. */
+	  inp->get_byte();		/* Product name len */
+	  server_product_name = inp->get_string((len-10)/2);
+	  int product_version = inp->get_int_be();
 	  if ((major == 4) && (minor == 2) &&
 	      ((product_version & 0xff0000ff) == 0x5f0000ff)) {
 	    // Workaround for bugs in MSSQL 6.5 & 7.0 for TDS 4.2.
@@ -1236,7 +1249,7 @@ static {
 				major, minor);
 	  break;
 	default:
-	  process_default_tokens(token_type);
+	  process_default_tokens(inp, token_type);
 	  break;
 	}
       } while (token_type != TDS_DONE_TOKEN);
@@ -1245,13 +1258,14 @@ static {
       TDS_WERROR("Login ok!\n");
     }
 
-    void submit_query(compile_query query, void|array(mixed) params)
+    InPacket submit_query(compile_query query, void|array(mixed) params)
     {
       Packet p = Packet();
       if (!query->n_param || !params || !sizeof(params)) {
 	string raw = query->splitted_query*"?";
 	p->put_raw(raw, sizeof(raw));
 	send_packet(p, 0x01, 1);
+	return InPacket();
       } else {
 	tds_error("parametrized queries not supported yet.\n");
       }
@@ -1289,8 +1303,7 @@ static {
 	socket = 0;
 	tds_error("Failed to connect to %s:%d\n", server, port);
       }
-      send_login();
-      process_login_tokens();
+      process_login_tokens(send_login());
     }
   }
 
@@ -1313,21 +1326,22 @@ static {
     con = Connection(server, port, database, uid, password);
   }
 
-  void Execute(compile_query query)
+  Connection.InPacket Execute(compile_query query)
   {
     query->parse_prepared_query();
     if (busy) {
       tds_error("Connection not idle.\n");
     }
+    Connection.InPacket res;
     if (!query->params) {
-      con->submit_query(query);
+      res = con->submit_query(query);
     } else {
-      con->submit_execdirect(query, query->params);
+      res = con->submit_execdirect(query, query->params);
     }
     int done = 0;
     int res_type;
     while (!done) {
-      res_type = con->process_result_tokens();
+      res_type = con->process_result_tokens(res);
       switch(res_type) {
       case TDS_COMPUTE_RESULT:
       case TDS_ROW_RESULT:
@@ -1342,6 +1356,7 @@ static {
     switch(res_type) {
       //case TDS_NO_MORE_RESULTS:
     }
+    return res;
   }
 #if (__REAL_MAJOR__ > 7) || ((__REAL_MAJOR__ == 7) && (__REAL_MINOR__ >= 6))
 };
@@ -1428,11 +1443,13 @@ class big_query
   static int row_no;
   static int eot;
 
+  static Connection.InPacket result_packet;
+
   int|array(string|int) fetch_row()
   {
     if (eot) return 0;
     TDS_WERROR("fetch_row()::::::::::::::::::::::::::::::::::::::::::\n");
-    int|array(string|int) res = con->process_row_tokens();
+    int|array(string|int) res = con->process_row_tokens(result_packet);
     eot = !res;
     row_no++;
     return res;
@@ -1441,7 +1458,7 @@ class big_query
   array(mapping(string:mixed)) fetch_fields()
   {
     TDS_WERROR("fetch_fields()::::::::::::::::::::::::::::::::::::::::::\n");
-    return copy_value(column_info);
+    return copy_value(column_info || ({}));
   }
 
   static void create(string|compile_query query)
@@ -1449,7 +1466,7 @@ class big_query
     if (stringp(query)) {
       query = compile_query(query);
     }
-    Execute(query);
+    result_packet = Execute(query);
   }
 }
 
-- 
GitLab