📄 query.c
字号:
tds_convert_string_free((char*)src, s); } else { /* TODO ICONV handle charset conversions for data */ /* put size of data */ switch (curcol->column_varint_size) { case 4: /* It's a BLOB... */ blob = (TDSBLOB *) curcol->column_data; tds_put_byte(tds, 16); tds_put_n(tds, blob->textptr, 16); tds_put_n(tds, blob->timestamp, 8); colsize = MIN(colsize, 0x7fffffff); tds_put_int(tds, colsize); break; case 2: colsize = MIN(colsize, 8000); tds_put_smallint(tds, colsize); break; case 1: if (is_numeric_type(curcol->column_type)) colsize = tds_numeric_bytes_per_prec[((TDS_NUMERIC *) src)->precision]; colsize = MIN(colsize, 255); tds_put_byte(tds, colsize); break; case 0: /* TODO should be column_size */ colsize = tds_get_size_by_type(curcol->column_type); break; } /* put real data */ if (is_numeric_type(curcol->column_type)) { num = (TDS_NUMERIC *) src; tds_put_n(tds, num->array, colsize); } else if (is_blob_type(curcol->column_type)) { blob = (TDSBLOB *) src; /* FIXME ICONV handle conversion when needed */ tds_put_n(tds, blob->textvalue, colsize); } else {#ifdef WORDS_BIGENDIAN unsigned char buf[64]; if (tds->emul_little_endian && colsize < 64) { tdsdump_log(TDS_DBG_INFO1, "swapping coltype %d\n", tds_get_conversion_type(curcol->column_type, colsize)); memcpy(buf, src, colsize); tds_swap_datatype(tds_get_conversion_type(curcol->column_type, colsize), buf); src = buf; }#endif tds_put_n(tds, src, colsize); } } return TDS_SUCCEED;}static voidtds7_send_execute(TDSSOCKET * tds, TDSDYNAMIC * dyn){ TDSCOLUMN *param; TDSPARAMINFO *info; int i; /* procedure name */ tds_put_smallint(tds, 10); /* NOTE do not call this procedure using integer name (TDS_SP_EXECUTE) on mssql2k, it doesn't work! */ TDS_PUT_N_AS_UCS2(tds, "sp_execute"); tds_put_smallint(tds, 0); /* flags */ /* id of prepared statement */ tds_put_byte(tds, 0); tds_put_byte(tds, 0); tds_put_byte(tds, SYBINTN); tds_put_byte(tds, 4); tds_put_byte(tds, 4); tds_put_int(tds, dyn->num_id); info = dyn->params; if (info) for (i = 0; i < info->num_cols; i++) { param = info->columns[i]; /* TODO check error */ tds_put_data_info(tds, param, 0); tds_put_data(tds, param); } tds->internal_sp_called = TDS_SP_EXECUTE;}/** * tds_submit_execute() sends a previously prepared dynamic statement to the * server. * \param tds state information for the socket and the TDS protocol * \param dyn dynamic proc to execute. Must build from same tds. */inttds_submit_execute(TDSSOCKET * tds, TDSDYNAMIC * dyn){ int id_len; CHECK_TDS_EXTRA(tds); /* TODO this dynamic should be in tds */ CHECK_DYNAMIC_EXTRA(dyn); tdsdump_log(TDS_DBG_FUNC, "tds_submit_execute()\n"); if (tds_set_state(tds, TDS_QUERYING) != TDS_QUERYING) return TDS_FAIL; tds->cur_dyn = dyn; if (IS_TDS7_PLUS(tds)) { /* check proper id */ if (dyn->num_id == 0) { tds_set_state(tds, TDS_IDLE); return TDS_FAIL; } /* RPC on sp_execute */ tds->out_flag = TDS_RPC; START_QUERY; tds7_send_execute(tds, dyn); return tds_query_flush_packet(tds); } if (dyn->emulated) { if (tds_send_emulated_execute(tds, dyn->query, dyn->params) != TDS_SUCCEED) return TDS_FAIL; return tds_query_flush_packet(tds); } /* query has been prepared successfully, discard original query */ if (dyn->query) TDS_ZERO_FREE(dyn->query); tds->out_flag = TDS_NORMAL; /* dynamic id */ id_len = strlen(dyn->id); tds_put_byte(tds, TDS5_DYNAMIC_TOKEN); tds_put_smallint(tds, id_len + 5); tds_put_byte(tds, 0x02); tds_put_byte(tds, dyn->params ? 0x01 : 0); tds_put_byte(tds, id_len); tds_put_n(tds, dyn->id, id_len); tds_put_smallint(tds, 0); if (dyn->params) tds_put_params(tds, dyn->params, 0); /* send it */ return tds_query_flush_packet(tds);}static voidtds_put_params(TDSSOCKET * tds, TDSPARAMINFO * info, int flags){ int i, len; CHECK_TDS_EXTRA(tds); CHECK_PARAMINFO_EXTRA(info); /* column descriptions */ tds_put_byte(tds, TDS5_PARAMFMT_TOKEN); /* size */ len = 2; for (i = 0; i < info->num_cols; i++) len += tds_put_data_info_length(tds, info->columns[i], flags); tds_put_smallint(tds, len); /* number of parameters */ tds_put_smallint(tds, info->num_cols); /* column detail for each parameter */ for (i = 0; i < info->num_cols; i++) { /* FIXME add error handling */ tds_put_data_info(tds, info->columns[i], flags); } /* row data */ tds_put_byte(tds, TDS5_PARAMS_TOKEN); for (i = 0; i < info->num_cols; i++) { tds_put_data(tds, info->columns[i]); }}static volatile int inc_num = 1;/** * Get an id for dynamic query based on TDS information * \param tds state information for the socket and the TDS protocol * \return TDS_FAIL or TDS_SUCCEED */inttds_get_dynid(TDSSOCKET * tds, char **id){ unsigned long n; int i; char *p; char c; CHECK_TDS_EXTRA(tds); inc_num = (inc_num + 1) & 0xffff; /* some version of Sybase require length <= 10, so we code id */ n = (unsigned long) tds; if (!(p = (char *) malloc(16))) return TDS_FAIL; *id = p; *p++ = (char) ('a' + (n % 26u)); n /= 26u; for (i = 0; i < 9; ++i) { c = (char) ('0' + (n % 36u)); *p++ = (c < ('0' + 10)) ? c : c + ('a' - '0' - 10); /* printf("%d -> %d(%c)\n",n%36u,p[-1],p[-1]); */ n /= 36u; if (i == 4) n += 3u * inc_num; } *p++ = 0; return TDS_SUCCEED;}/** * Send a unprepare request for a prepared query * \param tds state information for the socket and the TDS protocol * \param dyn dynamic query * \result TDS_SUCCEED or TDS_FAIL */inttds_submit_unprepare(TDSSOCKET * tds, TDSDYNAMIC * dyn){ int id_len; CHECK_TDS_EXTRA(tds); /* TODO test dyn in tds */ CHECK_DYNAMIC_EXTRA(dyn); if (!dyn) return TDS_FAIL; tdsdump_log(TDS_DBG_FUNC, "tds_submit_unprepare() %s\n", dyn->id); if (tds_set_state(tds, TDS_QUERYING) != TDS_QUERYING) return TDS_FAIL; tds->cur_dyn = dyn; if (IS_TDS7_PLUS(tds)) { /* RPC on sp_execute */ tds->out_flag = TDS_RPC; START_QUERY; /* procedure name */ if (IS_TDS8_PLUS(tds)) { /* save some byte for mssql2k */ tds_put_smallint(tds, -1); tds_put_smallint(tds, TDS_SP_UNPREPARE); } else { tds_put_smallint(tds, 12); TDS_PUT_N_AS_UCS2(tds, "sp_unprepare"); } tds_put_smallint(tds, 0); /* flags */ /* id of prepared statement */ tds_put_byte(tds, 0); tds_put_byte(tds, 0); tds_put_byte(tds, SYBINTN); tds_put_byte(tds, 4); tds_put_byte(tds, 4); tds_put_int(tds, dyn->num_id); tds->internal_sp_called = TDS_SP_UNPREPARE; return tds_query_flush_packet(tds); } if (dyn->emulated) { tds->out_flag = TDS_QUERY; START_QUERY; /* just a dummy select to return some data */ tds_put_string(tds, "select 1 where 0=1", -1); return tds_query_flush_packet(tds); } tds->out_flag = TDS_NORMAL; /* dynamic id */ id_len = strlen(dyn->id); tds_put_byte(tds, TDS5_DYNAMIC_TOKEN); tds_put_smallint(tds, id_len + 5); tds_put_byte(tds, 0x04); tds_put_byte(tds, 0x00); tds_put_byte(tds, id_len); tds_put_n(tds, dyn->id, id_len); tds_put_smallint(tds, 0); /* send it */ return tds_query_flush_packet(tds);}/** * tds_submit_rpc() call a RPC from server. Output parameters will be stored in tds->param_info * \param tds state information for the socket and the TDS protocol * \param rpc_name name of RPC * \param params parameters informations. NULL for no parameters */inttds_submit_rpc(TDSSOCKET * tds, const char *rpc_name, TDSPARAMINFO * params){ TDSCOLUMN *param; int rpc_name_len, i; int num_params = params ? params->num_cols : 0; CHECK_TDS_EXTRA(tds); if (params) CHECK_PARAMINFO_EXTRA(params); assert(tds); assert(rpc_name); if (tds_set_state(tds, TDS_QUERYING) != TDS_QUERYING) return TDS_FAIL; /* distinguish from dynamic query */ tds->cur_dyn = NULL; rpc_name_len = strlen(rpc_name); if (IS_TDS7_PLUS(tds)) { const char *converted_name; int converted_name_len; tds->out_flag = TDS_RPC; /* procedure name */ converted_name = tds_convert_string(tds, tds->char_convs[client2ucs2], rpc_name, rpc_name_len, &converted_name_len); if (!converted_name) { tds_set_state(tds, TDS_IDLE); return TDS_FAIL; } START_QUERY; tds_put_smallint(tds, converted_name_len / 2); tds_put_n(tds, converted_name, converted_name_len); tds_convert_string_free(rpc_name, converted_name); /* * TODO support flags * bit 0 (1 as flag) in TDS7/TDS5 is "recompile" * bit 1 (2 as flag) in TDS7+ is "no metadata" bit * (I don't know meaning of "no metadata") */ tds_put_smallint(tds, 0); for (i = 0; i < num_params; i++) { param = params->columns[i]; /* TODO check error */ tds_put_data_info(tds, param, TDS_PUT_DATA_USE_NAME); tds_put_data(tds, param); } return tds_query_flush_packet(tds); } if (IS_TDS50(tds)) { tds->out_flag = TDS_NORMAL; /* DBRPC */ tds_put_byte(tds, TDS_DBRPC_TOKEN); /* TODO ICONV convert rpc name */ tds_put_smallint(tds, rpc_name_len + 3); tds_put_byte(tds, rpc_name_len); tds_put_n(tds, rpc_name, rpc_name_len); /* TODO flags */ tds_put_smallint(tds, num_params ? 2 : 0); if (num_params) tds_put_params(tds, params, TDS_PUT_DATA_USE_NAME); /* send it */ return tds_query_flush_packet(tds); } /* TODO emulate it for TDS4.x, send RPC for mssql */ /* TODO continue, support for TDS4?? */ tds_set_state(tds, TDS_IDLE); return TDS_FAIL;}/** * tds_send_cancel() sends an empty packet (8 byte header only) * tds_process_cancel should be called directly after this. * \param tds state information for the socket and the TDS protocol * \remarks * tcp will either deliver the packet or time out. * (TIME_WAIT determines how long it waits between retries.) * * On sending the cancel, we may get EAGAIN. We then select(2) until we know * either 1) it succeeded or 2) it didn't. On failure, close the socket, * tell the app, and fail the function. * * On success, we read(2) and wait for a reply with select(2). If we get * one, great. If the client's timeout expires, we tell him, but all we can * do is wait some more or give up and close the connection. If he tells us * to cancel again, we wait some more. */inttds_send_cancel(TDSSOCKET * tds){ CHECK_TDS_EXTRA(tds); tdsdump_log(TDS_DBG_FUNC, "tds_send_cancel: %sin_cancel and %sidle\n", (tds->in_cancel? "":"not "), (tds->state == TDS_IDLE? "":"not ")); /* one cancel is sufficient */ if (tds->in_cancel || tds->state == TDS_IDLE) return TDS_SUCCEED; tds->out_flag = TDS_CANCEL; tds->in_cancel = 1; tdsdump_log(TDS_DBG_FUNC, "tds_send_cancel: sending cancel packet\n"); return tds_flush_packet(tds);}static inttds_quote(TDSSOCKET * tds, char *buffer, char quoting, const char *id, int len){ int i; const char *src, *pend; char *dst; CHECK_TDS_EXTRA(tds); pend = id + len; /* quote */ src = id; if (!buffer) { i = 2 + len; for (; src != pend; ++src) if (*src == quoting) ++i; return i; } dst = buffer; *dst++ = (quoting == ']') ? '[' : quoting; for (; src != pend; ++src) { if (*src == quoting) *dst++ = quoting; *dst++ = *src; } *dst++ = quoting; *dst = 0; return dst - buffer;}/** * Quote an id * \param tds state information for the socket and the TDS protocol * \param buffer buffer to store quoted id. If NULL do not write anything * (useful to compute quote length) * \param id id to quote * \param idlen id length * \result written chars (not including needed terminator) */inttds_quote_id(TDSSOCKET * tds, char *buffer, const char *id, int idlen){ int i; CHECK_TDS_EXTRA(tds); if (idlen < 0) idlen = strlen(id); /* need quote ?? */ for (i = 0; i < idlen; ++i) { char c = id[i]; if (c >= 'a' && c <= 'z') continue; if (c >= 'A' && c <= 'Z') continue; if (i > 0 && c >= '0' && c <= '9') continue; if (c == '_') continue; return tds_quote(tds, buffer, TDS_IS_MSSQL(tds) ? ']' : '\"', id, idlen); } if (buffer) { memcpy(buffer, id, idlen); buffer[idlen] = '\0'; } return idlen;}/** * Quote a string * \param tds state information for the socket and the TDS protocol * \param buffer buffer to store quoted id. If NULL do not write anything * (useful to compute quote length) * \param str string to quote (not necessary null-terminated) * \param len length of string (-1 for null terminated) * \result written chars (not including needed terminator) */inttds_quote_string(TDSSOCKET * tds, char *buffer, const char *str, int len){ return tds_quote(tds, buffer, '\'', str, len < 0 ? strlen(str) : len);}static inline void
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -