[freetds] [PATCH] Problem with VARCHAR in sybase.
Eddy Pronk
epronk at muftor.com
Thu Jul 3 18:38:05 EDT 2008
James K. Lowden wrote:
> Thank you for the patch. You've highlighted a problem, but I don't think
> your fix is the right one.
>
> In the first place, ct-lib needs some time and attention paid to error
> messages. There are many places where it works if used correctly but
> fails silently (except for the return code) if not. We need a function
> similar to dbperror().
>
> Second, afaict CS_FMT_NULLTERM is invalid in cs_convert (cf.
> http://manuals.sybase.com:80/onlinebooks/group-oc/ocg1250e/occpr/@ebt-link;pt=2420?target=%25N%14_2686_START_RESTART_N%25).
>
>
> Third, even if valid, istm the code is basically right: if the destination
> buffer does not have room for a NULL terminator and the format
> specification for the conversion demands one, the conversion should fail.
>
>
> If I'm missing something, please explain.
>
I isolated the way libdbi uses freetds. (modified array_bind.c)
It demonstrates ct_fetch fails before the client code knows about the
lengh (20)
without datafmt.format = CS_FMT_NULLTERM; it doesn't put the '\0'
08:31:15.949986 ct_bind() datafmt count = 1 column_number = 1
08:31:15.949995 ct_fetch()
08:31:15.950003 processing row tokens. marker is d1(ROW)
08:31:15.950012 processing row. column is 0 varint size = 1
08:31:15.950021 processing row. column size is 20
08:31:15.950029 clearing column 0 NULL bit
08:31:15.950038 inside ct_fetch()process_row_tokens returned 1
08:31:15.950046 _ct_bind_data()
08:31:15.950055 _ct_bind_data(): column_type: 39 column_len: 20
08:31:15.950063 _ct_get_server_type(1)
08:31:15.950072 _ct_get_client_type(type 39, user 2, size 20)
08:31:15.950081 cs_convert()
08:31:15.950088 _ct_get_server_type(1)
08:31:15.950096 _ct_get_server_type(1)
08:31:15.950105 cs_convert() srctype = 47 (20) desttype = 47 (20)
08:31:15.950114 cs_convert() srctype = desttype
08:31:15.950122 cs_convert() desttype = character
08:31:15.950130 not enough room for data + a null terminator - error
08:31:15.950138 cs_convert-result = 1
08:31:15.950146
convert failed for 1
maxlength = 20
datalength = 0
ct_fetch() 1 row 20.
#if HAVE_CONFIG_H
#include <config.h>
#endif /* HAVE_CONFIG_H */
#if HAVE_STRING_H
#include <string.h>
#endif /* HAVE_STRING_H */
#include <stdio.h>
#include <ctpublic.h>
#include "common.h"
static char software_version[] = "$Id: array_bind.c,v 1.3 2004/01/31
16:07:14 freddy77 Exp $";
static void *no_unused_var_warn[] = { software_version,
no_unused_var_warn };
/* Testing: array binding of result set */
int
main(int argc, char *argv[])
{
CS_CONTEXT *ctx;
CS_CONNECTION *conn;
CS_COMMAND *cmd;
int verbose = 0;
CS_RETCODE ret;
CS_RETCODE results_ret;
CS_INT result_type;
CS_INT num_cols;
CS_DATAFMT datafmt;
CS_INT datalength = 0;
CS_SMALLINT ind = 0;
CS_INT count, row_count = 0;
CS_INT cv;
CS_CHAR select[1024];
CS_INT col1[2];
void *addr = NULL;
fprintf(stdout, "%s: Retrieve data using array binding \n", __FILE__);
if (verbose) {
fprintf(stdout, "Trying login\n");
}
ret = try_ctlogin(&ctx, &conn, &cmd, verbose);
if (ret != CS_SUCCEED) {
fprintf(stderr, "Login failed\n");
return 1;
}
strcpy(select, "select name from destination");
ret = ct_command(cmd, CS_LANG_CMD, select, CS_NULLTERM, CS_UNUSED);
if (ret != CS_SUCCEED) {
fprintf(stderr, "ct_command(%s) failed\n", select);
return 1;
}
ret = ct_send(cmd);
if (ret != CS_SUCCEED) {
fprintf(stderr, "ct_send() failed\n");
return 1;
}
while ((results_ret = ct_results(cmd, &result_type)) == CS_SUCCEED) {
switch ((int) result_type) {
case CS_CMD_SUCCEED:
break;
case CS_CMD_DONE:
break;
case CS_CMD_FAIL:
fprintf(stderr, "ct_results() result_type CS_CMD_FAIL.\n");
return 1;
case CS_ROW_RESULT:
ret = ct_res_info(cmd, CS_NUMDATA, &num_cols, CS_UNUSED, NULL);
if (ret != CS_SUCCEED) {
fprintf(stderr, "ct_res_info() failed");
return 1;
}
fprintf(stderr, "%d cols\n", num_cols);
ret = ct_describe(cmd, 1, &datafmt);
if (ret != CS_SUCCEED) {
fprintf(stderr, "ct_describe() failed\n");
return 1;
}
fprintf(stderr, "type = %d\n", datafmt.datatype);
fprintf(stderr, "maxlength = %d\n", datafmt.maxlength);
if(datafmt.datatype == CS_CHAR_TYPE)
{
fprintf(stderr, "CS_CHAR_TYPE\n");
datafmt.format = CS_FMT_NULLTERM;
addr = malloc(datafmt.maxlength);
}
fprintf(stderr, "bind\n");
fprintf(stderr, "datalength = %d\n", datalength);
ret = ct_bind(cmd, 1, &datafmt, addr, &datalength, &ind);
if (ret != CS_SUCCEED) {
fprintf(stderr, "ct_bind() failed\n");
return 1;
}
while (((ret = ct_fetch(cmd, CS_UNUSED, CS_UNUSED,
CS_UNUSED, &count)) == CS_SUCCEED)
|| (ret == CS_ROW_FAIL)) {
fprintf(stderr, "maxlength = %d\n", datafmt.maxlength);
fprintf(stderr, "datalength = %d\n", datalength);
fprintf(stderr, "ct_fetch() 1 row %d.\n", row_count);
fprintf(stderr, "[%s]\n", addr);
row_count += count;
if (ret == CS_ROW_FAIL) {
fprintf(stderr, "ct_fetch() CS_ROW_FAIL on row
%d.\n", row_count);
return 1;
} else { /* ret == CS_SUCCEED */
fprintf(stdout, "ct_fetch returned %d rows\n", count);
}
count = 0;
ret = ct_bind(cmd, 1, &datafmt, addr, &datalength, &ind);
}
switch ((int) ret) {
case CS_END_DATA:
break;
case CS_FAIL:
fprintf(stderr, "ct_fetch() returned CS_FAIL.\n");
return 1;
default:
fprintf(stderr, "ct_fetch() unexpected return.\n");
return 1;
}
break;
default:
fprintf(stderr, "ct_results() unexpected result_type.\n");
return 1;
}
}
switch ((int) results_ret) {
case CS_END_RESULTS:
break;
case CS_FAIL:
fprintf(stderr, "ct_results() failed.\n");
return 1;
break;
default:
fprintf(stderr, "ct_results() unexpected return.\n");
return 1;
}
if (verbose) {
fprintf(stdout, "Trying logout\n");
}
ret = try_ctlogout(ctx, conn, cmd, verbose);
if (ret != CS_SUCCEED) {
fprintf(stderr, "Logout failed\n");
return 1;
}
return 0;
}
Eddy
More information about the FreeTDS
mailing list