pike.git / lib / modules / Sql.pmod / tds.pike

version» Context lines:

pike.git/lib/modules/Sql.pmod/tds.pike:1:   /* -  * $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.    *    * Henrik Grubbström 2006-02-08.    */      #define TDS_DEBUG      #ifdef TDS_DEBUG   #define TDS_WERROR(X...) werror("TDS:" + X)
pike.git/lib/modules/Sql.pmod/tds.pike:210:    string database = "";    string domain;    string capabilities = DEFAULT_CAPABILITIES;       Stdio.File socket;      #define FMT_SMALLINT "%-2c"   #define FMT_INT "%-4c"   #define FMT_INT8 "%-4c"    +  class InPacket +  {    int inpos = 0;    string inbuf = ""; -  +  int done; +     static void fill_buf()    { -  if (!busy) { -  TDS_WERROR("Filling buffer on idle connection!\n"); +  if (done) { +  TDS_WERROR("Filling buffer on finished packet!\n" +  "%s\n", hex_dump(inbuf));    }       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 = !last_packet; +  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;
pike.git/lib/modules/Sql.pmod/tds.pike:304:       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 = ({});    array(string) strings = ({});    int flags;       static void create(int|void flags)    {    this_program::flags = flags;    }
pike.git/lib/modules/Sql.pmod/tds.pike:420:    }    }       static void send_packet(Packet p, int flag, int|void last)    {    string raw = (string) p;       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!       // NOTE: Network byteorder!!    raw =    sprintf("%-1c%-1c%2c\0\0%-1c\0%s",    flag, last,    sizeof(raw) + 8,    1, /* TDS 7 or 8. */    (string)raw);    TDS_WERROR("Wrapped packet: %O\n%s\n", raw, hex_dump(raw));    if (socket->write(raw) != sizeof(raw)) {    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)    {    password = string_to_utf16(password);    password ^= "\x5a"*sizeof(password);    return (string)map((array)password,    lambda(int byte) {    return ((byte >> 4) | (byte << 4)) & 0xff;    });    }    -  static void send_login() +  static InPacket send_login()    {    password = password[..127];       Packet p = Packet(1);       p->put_raw(TDS8VERSION, 4); // TDS version    p->put_int(0); // block size    p->put_raw(CLIENTVERSION, 4); // client version    p->put_int(getpid()); // pid    p->put_raw(CONNECTIONID, 4); // connection identifier
pike.git/lib/modules/Sql.pmod/tds.pike:501:    p->put_raw("\0"*6, 6); // MAC address    if (domain) {    p->put_domain_login(domain, hostname);    } else {    p->put_string("");    }    p->put_string("");       TDS_WERROR("Sending login packet.\n");    send_packet(p, 0x10, 1); +  return InPacket();    }       string ecb_encrypt(string data, string key)    {   #if constant(Crypto.DES)    Crypto.DES des = Crypto.DES();    des->set_encrypt_key(des->fix_parity(key));   #else    Crypto.des des = Crypto.des();    des->set_encrypt_key(Crypto.des_parity(key));
pike.git/lib/modules/Sql.pmod/tds.pike:564:    p->put_long_string(domain);    p->put_long_string(username);    p->put_long_string(hostname);    p->put_long_string(""); /* Unknown */    p->put_int(0x8201); /* flags */       TDS_WERROR("Sending auth packet.\n");    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) {    case TDS_EED_TOKEN:    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");    break;    case TDS_ERROR_TOKEN:    TDS_WERROR("TDS_ERROR_TOKEN\n");    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;    }    }    if (is_error) {    tds_error("%d: %s:%s:%d %s\n",    level, proc_name, server, line, message);    } else {    TDS_WERROR("%d: %s:%s:%d %s\n",    level, proc_name, server, line, message);    }    }    -  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;    sscanf(collation, "%-3c", lcid);    /* 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;    TDS_WERROR("Change block size from %O to %O\n", old_val, new_val);    break;    case TDS_ENV_DATABASE:    TDS_WERROR("Database changed to %O\n", new_val);    break;    case TDS_ENV_LANG:    TDS_WERROR("Language changed to %O from %O\n", new_val, old_val);
pike.git/lib/modules/Sql.pmod/tds.pike:791:    (col_type==XSYBNVARCHAR) ||    (col_type==XSYBNCHAR) ||    (col_type==SYBNTEXT);    }       int is_numeric_type(int col_type)    {    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);    res->identity == !!(res->flags & 0x10);       res->cardinal_type = get_cardinal_type(res->column_type);    res->varint_size = get_varint_size(res->column_type);    TDS_WERROR("Intermediate info: %O\n", res);    switch(res->varint_size) {    case 0:    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);       res->timestamp = (res->cardinal_type == SYBBINARY) ||    (res->cardinal_type == TDS_UT_TIMESTAMP);       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;    return;    }    TDS_WERROR("%d columns in result.\n", num_cols);    column_info = allocate(num_cols);    //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)) {    offset += 35; // sizeof(TDS_NUMERIC)    } else if (is_blob_type(column_info[col]->cardinal_type)) {    offset += 32; // sizeof(TDSBLOB)    } else {    offset += column_info[col]->column_size;    }    offset += (~(offset - 1) & 0x3); // Align.    TDS_WERROR("Next offset: 0x%04x\n", offset);    }       }    -  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) {    case TDS_DONE_TOKEN:    return;    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:    case TDS_ORDERBY_TOKEN:    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",    token_type);    break;    }    }    -  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!    }    }    TDS_WERROR("==> TDS_ROWFMT_RESULT\n");    return TDS_ROWFMT_RESULT;    case TDS_DONE_TOKEN:    case TDS_ROW_TOKEN:    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;    int colsize = 0;       outer:    switch(info->varint_size) {    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 "";    }    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);    break;    }    TDS_WERROR("Column size is %d\n", colsize);    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)) {    raw = utf16_to_string(raw);    }    if (colsize < info->column_size) {    // Handle padding.    switch(info->cardinal_type) {    case SYBLONGBINARY:    // FIXME: ??
pike.git/lib/modules/Sql.pmod/tds.pike:1143:    year += (century + 15)*100 + l;    if (!l && !(year & 3) && ((year % 100) || !(year % 400)))    yday++;    return sprintf("%04d-%02d-%02dT%02d:%02d:%02d",    year, mon, mday, hour, min, sec);    }    }    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:    case TDS_ROWFMT2_TOKEN:    case TDS7_RESULT_TOKEN:    // Some other result starts here.    //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:    TDS_WERROR("TDS_DONE_TOKEN\n");    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.    product_version = (product_version & 0xffff00) | 0x800000000;    }       // TDS 5.0 reports 5 on success 6 on fail.    // TDS 4.2 reports 1 on success and N/A on failure.    if ((ack == 5) || (ack == 1)) ok = 1;    TDS_WERROR(" ok:%d ack:%d %s major:%d minor:%d version:%08x\n",    ok, ack,    server_product_name, major, minor, product_version);    server_product_name = (server_product_name/"\0")[0];    server_data = sprintf("%s %d.%d.%d.%d",    server_product_name,    product_version>>24,    product_version & 0xffffff,    major, minor);    break;    default: -  process_default_tokens(token_type); +  process_default_tokens(inp, token_type);    break;    }    } while (token_type != TDS_DONE_TOKEN);    //if (!(spid = rows_affected)) set_spid();    if (!ok) tds_error("Login failed.\n");    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");    }    }       static void disconnect()    {    socket->close();    destruct();    }
pike.git/lib/modules/Sql.pmod/tds.pike:1282:    this_program::domain = domain;       TDS_WERROR("Connecting to %s:%d version %d.%d\n",    server, port, major_version, minor_version);       socket = Stdio.File();    if (!socket->connect(server, port)) {    socket = 0;    tds_error("Failed to connect to %s:%d\n", server, port);    } -  send_login(); -  process_login_tokens(); +  process_login_tokens(send_login());    }    }       object(Connection) con;       int affected_rows;    string server_product_name = "";       int busy;   
pike.git/lib/modules/Sql.pmod/tds.pike:1306:    con->disconnect();    con = 0;    }       void Connect(string server, int port, string database,    string uid, string password)    {    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:    done = 1;    break;    default:    TDS_WERROR("res_type: %d\n", res_type);    break;    }    }    // populate_ird    switch(res_type) {    //case TDS_NO_MORE_RESULTS:    } -  +  return res;    }   #if (__REAL_MAJOR__ > 7) || ((__REAL_MAJOR__ == 7) && (__REAL_MINOR__ >= 6))   };   #endif /* Pike 7.6 or later */      class compile_query   {    int n_param;    array(string) splitted_query;   
pike.git/lib/modules/Sql.pmod/tds.pike:1421:    splitted_query = split_query_on_placeholders(query);    n_param = sizeof(splitted_query)-1;    }   }      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;    }       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)    {    if (stringp(query)) {    query = compile_query(query);    } -  Execute(query); +  result_packet = Execute(query);    }   }      string server_info()   {    return server_data;   }      string error()   {