Re: [PATCH] Problem with VARCHAR in sybase.

Eddy Pronk <[email protected]>
Newsgroups gmane.comp.db.tds.freetds
Message-ID <[email protected]>
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
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.