Skip to Content.
Sympa Menu

freetds - Re: [freetds] [PATCH] Problem with VARCHAR in sybase.

freetds AT lists.ibiblio.org

Subject: FreeTDS Development Group

List archive

Chronological Thread  
  • From: Eddy Pronk <epronk AT muftor.com>
  • To: FreeTDS Development Group <freetds AT lists.ibiblio.org>
  • Subject: Re: [freetds] [PATCH] Problem with VARCHAR in sybase.
  • Date: Fri, 04 Jul 2008 08:38:05 +1000

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




Archive powered by MHonArc 2.6.24.

Top of Page