[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