Re: [PATCH] Problem with VARCHAR in sybase.
"James K. Lowden" <[email protected]>
| Newsgroups | gmane.comp.db.tds.freetds |
|---|---|
| Message-ID | <[email protected]> |
Eddy Pronk wrote: > 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. > > 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' Thanks for the code. I worked with it a little while and got it to run, which led me to look more deeply into ct-lib. I think the behavior is now correct. There were places it was setting the bound indicator variable (see below) to zero on failure when there was no need (and contrary to documented behavior). I believe you will find the attached test program fails with your version of ct-lib and works with CVS HEAD. Your version didn't work for me because my server has no table named "destination", so I changed the query to "select name from systypes". I was wrong about CS_FMT_NULLTERM being invalid for cs_convert, btw. A better link is http://infocenter.sybase.com/help/topic/com.sybase.help.ocs_12.5.1.comlib/html/comlib/X12687.htm. The basic question is: How should ct_fetch() react when the datafmt.maxlength argument indicates a length *exactly* equal to the length of the returned data and datafmt.format is CS_FMT_NULLTERM? ct_fetch should: 1. copy as many bytes as possible -- as fit in the bound destination buffers -- to each bound buffer. 2. set each buffer's indicator variable to reflect the bytes copied. 3. if any buffer was too small, return CS_ROW_FAIL. In the test case, ct_fetch() fetches the value "uniqueidentifier", 16 bytes. The bound variable's datafmt.maxlength is set to 16, and datafmt.format is CS_FMT_NULLTERM. The buffer needs to provide 17 bytes: 16 data plus the null terminator. But it's offering only 16. ct_fetch() copies 16 bytes, discovers there's no room for the NULL terminator, and returns the error. Exactly as should be. I'm interested to hear if CVS HEAD resolves the original problem. Regards, --jkl _______________________________________________ FreeTDS mailing list [email protected] http://lists.ibiblio.org/mailman/listinfo/freetds
datafmt.c
(application/octet-stream, 3.8 KB)
#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 <stdlib.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 copied = 0;
CS_SMALLINT ind = 0;
CS_INT count, row_count = 0;
CS_CHAR select[1024];
char *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 systypes where datalength(name) > 2*9 order by datalength(name)");
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, "binding column 1 (%s)\n", datafmt.name);
/* set maxlength to something short to test truncation behavior */
datafmt.maxlength = 16;
ret = ct_bind(cmd, 1, &datafmt, addr, &copied, &ind);
if (ret != CS_SUCCEED) {
fprintf(stderr, "ct_bind() failed\n");
return 1;
}
fprintf(stderr, "fetching rows with datafmt.maxlength = %d\n", datafmt.maxlength);
while ((ret = ct_fetch(cmd, CS_UNUSED, CS_UNUSED, CS_UNUSED, &count)) != CS_END_DATA)
fprintf(stderr, "ct_fetch() row %d returned %s.\n", row_count, cs_prretcode(ret));
fprintf(stderr, "copied %d bytes: [%s]\n", copied, addr);
row_count += count;
switch (ret) {
case CS_SUCCEED:
fprintf(stdout, "ct_fetch returned %d row%s\n", count, count==1? "":"s");
break;
case CS_ROW_FAIL:
fprintf(stderr, "error: ct_fetch() returned CS_ROW_FAIL on row %d.\n", row_count);
return 1;
case CS_CANCELED:
fprintf(stderr, "error: ct_fetch() returned CS_CANCELED??\n");
return 1;
case CS_FAIL:
fprintf(stderr, "error: ct_fetch() returned CS_FAIL.\n");
return 1;
default:
fprintf(stderr, "error: ct_fetch() unexpected return.\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;
}