"Fossies" - the Fresh Open Source Software Archive

Member "gretl-2020b/plugin/odbc_import.c" (24 Mar 2020, 21402 Bytes) of package /linux/misc/gretl-2020b.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "odbc_import.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 2020a_vs_2020b.

    1 /*
    2  *  gretl -- Gnu Regression, Econometrics and Time-series Library
    3  *  Copyright (C) 2001 Allin Cottrell and Riccardo "Jack" Lucchetti
    4  *
    5  *  This program is free software: you can redistribute it and/or modify
    6  *  it under the terms of the GNU General Public License as published by
    7  *  the Free Software Foundation, either version 3 of the License, or
    8  *  (at your option) any later version.
    9  *
   10  *  This program is distributed in the hope that it will be useful,
   11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
   12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13  *  GNU General Public License for more details.
   14  *
   15  *  You should have received a copy of the GNU General Public License
   16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
   17  *
   18  */
   19 
   20 #include "libgretl.h"
   21 #include "version.h"
   22 #include "dbread.h"
   23 
   24 #ifdef WIN32
   25 # include "gretl_win32.h"
   26 #endif
   27 
   28 #include <sql.h>
   29 #include <sqlext.h>
   30 #include <sqltypes.h>
   31 
   32 #define ODBC_INIT_ROWS 256
   33 
   34 #define OD_error(r) (r != SQL_SUCCESS && r != SQL_SUCCESS_WITH_INFO)
   35 
   36 #define DSN_LIST 0 /* maybe later */
   37 
   38 #if DSN_LIST
   39 
   40 #include <odbc/odbcinst.h>
   41 #include <odbc/odbcinstext.h>
   42 
   43 /* from unixODBC's ini.h */
   44 int iniElement (char *data, char sep, char term, int i,
   45         char *name, int len);
   46 
   47 #define INI_SUCCESS 1
   48 
   49 static int show_list (void)
   50 {
   51     char inifile[FILENAME_MAX + 1] = "ODBC.INI";
   52     char section_names[4095] = {0};
   53     int sqlret;
   54     int err = 0;
   55 
   56     SQLSetConfigMode(ODBC_BOTH_DSN);
   57     sqlret = SQLGetPrivateProfileString(NULL, NULL, NULL,
   58                     section_names, sizeof section_names,
   59                     inifile);
   60 
   61     if (sqlret >= 0) {
   62     char driver[INI_MAX_OBJECT_NAME + 1];
   63     char desc[INI_MAX_OBJECT_NAME + 1];
   64     char sect_name[INI_MAX_OBJECT_NAME + 1];
   65     int i, iniret;
   66 
   67     printf("Listing of DSNs:\n");
   68 
   69     for (i=0; ; i++) {
   70         iniret = iniElement(section_names, '\0', '\0', i, sect_name,
   71                 INI_MAX_OBJECT_NAME);
   72         if (iniret != INI_SUCCESS) {
   73         break;
   74         }
   75         *driver = '\0';
   76         *desc = '\0';
   77 
   78         SQLGetPrivateProfileString(sect_name, "Driver", "", driver,
   79                        INI_MAX_PROPERTY_VALUE, inifile);
   80 
   81         SQLGetPrivateProfileString(sect_name, "Description", "", desc,
   82                        INI_MAX_PROPERTY_VALUE, inifile);
   83 
   84         printf("%s (%s): %s\n", sect_name, driver, desc);
   85     }
   86     } else {
   87     fprintf(stderr, "Couldn't load %s\n", inifile);
   88     err = 1;
   89     }
   90 
   91     return err;
   92 }
   93 
   94 #endif /* DSN_LIST */
   95 
   96 /* we got more data that we initially allocated space for; so
   97    expand the space available */
   98 
   99 static int expand_catchment (ODBC_info *odinfo, int *nrows)
  100 {
  101     int err, n = 2 * *nrows;
  102 
  103     err = doubles_array_adjust_length(odinfo->X, odinfo->nvars, n);
  104     if (err) {
  105     return err;
  106     }
  107 
  108     if (odinfo->S != NULL) {
  109     odinfo->S = strings_array_realloc_with_length(&odinfo->S,
  110                               *nrows, n,
  111                               OBSLEN);
  112     if (odinfo->S == NULL) {
  113         err = E_ALLOC;
  114     }
  115     }
  116 
  117     if (!err) {
  118     *nrows = n;
  119     }
  120 
  121     return err;
  122 }
  123 
  124 static const char *sql_status (SQLRETURN ret)
  125 {
  126     if (ret == SQL_SUCCESS) {
  127     return "SQL_SUCCESS";
  128     } else if (ret == SQL_SUCCESS_WITH_INFO) {
  129     return "SQL_SUCCESS_WITH_INFO";
  130     } else if (ret == SQL_ERROR) {
  131     return "SQL_ERROR";
  132     } else if (ret == SQL_INVALID_HANDLE) {
  133     return "SQL_INVALID_HANDLE";
  134     } else {
  135     return "??";
  136     }
  137 }
  138 
  139 /* Try connecting to data source.  If @penv is NULL we're just checking
  140    that it can be opened OK, otherwise we return a connection.
  141 */
  142 
  143 static SQLHDBC
  144 gretl_odbc_connect_to_dsn (ODBC_info *odinfo, SQLHENV *penv,
  145                PRN *prn, int *err)
  146 {
  147     SQLHENV OD_env = NULL;    /* ODBC environment handle */
  148     SQLHDBC dbc = NULL;       /* connection handle */
  149     SQLRETURN ret;            /* return value from functions */
  150     unsigned char status[10]; /* SQL status */
  151     SQLINTEGER OD_err;
  152     SQLSMALLINT mlen;
  153     char *uname, *pword;
  154     unsigned char msg[512];
  155 
  156     ret = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &OD_env);
  157     if (OD_error(ret)) {
  158     gretl_errmsg_set("Error in SQLAllocHandle for ENV");
  159     *err = 1;
  160     goto bailout;
  161     }
  162 
  163     ret = SQLSetEnvAttr(OD_env, SQL_ATTR_ODBC_VERSION,
  164             (void *) SQL_OV_ODBC3, 0);
  165     if (OD_error(ret)) {
  166     gretl_errmsg_set("Error in SQLSetEnvAttr");
  167     *err = 1;
  168     goto bailout;
  169     }
  170 
  171     ret = SQLAllocHandle(SQL_HANDLE_DBC, OD_env, &dbc);
  172     if (OD_error(ret)) {
  173     gretl_errmsg_set("Error in SQLAllocHandle for DBC");
  174     *err = 1;
  175     goto bailout;
  176     }
  177 
  178     SQLSetConnectAttr(dbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER *) 5, 0);
  179 
  180     /* Try connecting to the datasource */
  181 
  182     uname = odinfo->username;
  183     pword = odinfo->password;
  184 
  185     ret = SQLConnect(dbc, (SQLCHAR *) odinfo->dsn, SQL_NTS,
  186              (SQLCHAR *) uname, (uname == NULL)? 0 : SQL_NTS,
  187              (SQLCHAR *) pword, (pword == NULL)? 0 : SQL_NTS);
  188 
  189     if (OD_error(ret)) {
  190     gretl_errmsg_set("Error in SQLConnect");
  191     SQLGetDiagRec(SQL_HANDLE_DBC, dbc, 1, status,
  192               &OD_err, msg, 512, &mlen);
  193     gretl_errmsg_set((char *) msg);
  194     pprintf(prn, " odinfo->dsn = '%s'\n", odinfo->dsn);
  195     pprintf(prn, " odinfo->username = '%s'\n", odinfo->username);
  196     *err = 1;
  197     } else if (prn != NULL) {
  198     pprintf(prn, "SQLConnect(dbc,...): %s\n", sql_status(ret));
  199     }
  200 
  201  bailout:
  202 
  203     if (*err || penv == NULL) {
  204     /* either we bombed out, or we're just checking and the handles
  205        are not really wanted */
  206     if (dbc != NULL) {
  207         ret = SQLDisconnect(dbc);
  208         pprintf(prn, "SQLDisconnect(dbc): %s\n", sql_status(ret));
  209         ret = SQLFreeHandle(SQL_HANDLE_DBC, dbc);
  210         pprintf(prn, "SQLFreeHandle(SQL_HANDLE_DBC, dbc): %s\n", sql_status(ret));
  211         dbc = NULL;
  212     }
  213     if (OD_env != NULL) {
  214         ret = SQLFreeHandle(SQL_HANDLE_ENV, OD_env);
  215         pprintf(prn, "SQLFreeHandle(SQL_HANDLE_ENV, OD_env): %s\n", sql_status(ret));
  216     }
  217     } else {
  218     *penv = OD_env;
  219     }
  220 
  221     return dbc;
  222 }
  223 
  224 int gretl_odbc_check_dsn (ODBC_info *odinfo)
  225 {
  226     int err = 0;
  227 
  228     gretl_odbc_connect_to_dsn(odinfo, NULL, NULL, &err);
  229 
  230     return err;
  231 }
  232 
  233 static double strval_to_double (ODBC_info *odinfo, const char *s,
  234                 int r, int c, int *err)
  235 {
  236     if (odinfo->gst != NULL) {
  237     return gretl_string_table_index(odinfo->gst, s, c, 0, NULL);
  238     } else if (numeric_string(s)) {
  239     return atof(s);
  240     } else {
  241     gretl_errmsg_sprintf(_("Expected numeric data, found string:\n"
  242                    "'%s' at row %d, column %d\n"),
  243                  s, r, c);
  244     *err = E_DATA;
  245     return NADBL;
  246     }
  247 }
  248 
  249 static double date_to_double (ODBC_info *odinfo, DATE_STRUCT *dv,
  250                   int r, int c, int *err)
  251 {
  252     gint16 y = dv->year;
  253     gint16 m = dv->month;
  254     gint16 d = dv->day;
  255 
  256     return 10000*y + 100*m + d;
  257 }
  258 
  259 static void obsbit_from_sql_date (char *targ, DATE_STRUCT *dv)
  260 {
  261     guint16 y = dv->year;
  262     guint16 m = dv->month;
  263     guint16 d = dv->day;
  264 
  265     if (y > 9999 || m > 12 || d > 31) {
  266     *targ = '\0';
  267     } else {
  268     sprintf(targ, "%04d-%02d-%02d", (int) y, (int) m, (int) d);
  269     }
  270 }
  271 
  272 static int odbc_read_rows (ODBC_info *odinfo,
  273                SQLHSTMT stmt,
  274                int totcols,
  275                SQLLEN *colbytes,
  276                long *grabint,
  277                double *grabx,
  278                char **grabstr,
  279                DATE_STRUCT *grabd,
  280                double *xt,
  281                DATE_STRUCT **dv,
  282                int *nrows,
  283                int *obsgot,
  284                char **strvals,
  285                PRN *prn)
  286 {
  287     char obsbit[OBSLEN];
  288     SQLRETURN ret;
  289     int verbose = (prn != NULL);
  290     int i, j, k, p, q, v;
  291     int t = 0, err = 0;
  292 
  293     ret = SQLFetch(stmt);
  294 
  295     while (ret == SQL_SUCCESS && !err) {
  296     j = k = p = q = v = 0;
  297     if (verbose) {
  298         pprintf(prn, "Fetch, row %d: ", t);
  299     }
  300     for (i=0; i<totcols && !err; i++) {
  301         if (verbose) {
  302         pprintf(prn, "col %d: ", i);
  303         }
  304         if (i < odinfo->obscols) {
  305         /* looking for obs identifier chunk(s) */
  306         *obsbit = '\0';
  307         if (colbytes[i] == SQL_NULL_DATA) {
  308             if (verbose) {
  309             pputs(prn, "null data");
  310             }
  311             continue; /* error? */
  312         }
  313         if (verbose) {
  314             pprintf(prn, "%d bytes", (int) colbytes[i]);
  315         }
  316         if (odinfo->coltypes[i] == GRETL_TYPE_INT) {
  317             sprintf(obsbit, odinfo->fmts[i], (int) grabint[j++]);
  318         } else if (odinfo->coltypes[i] == GRETL_TYPE_STRING) {
  319             sprintf(obsbit, odinfo->fmts[i], grabstr[k++]);
  320         } else if (odinfo->coltypes[i] == GRETL_TYPE_DATE) {
  321             obsbit_from_sql_date(obsbit, &grabd[q++]);
  322         } else if (odinfo->coltypes[i] == GRETL_TYPE_DOUBLE) {
  323             sprintf(obsbit, odinfo->fmts[i], grabx[p++]);
  324         }
  325         if (odinfo->S != NULL && *obsbit != '\0') {
  326             if (strlen(odinfo->S[t]) + strlen(obsbit) > OBSLEN - 1) {
  327             fprintf(stderr, "Overflow in observation string!\n");
  328             } else {
  329             strcat(odinfo->S[t], obsbit);
  330             }
  331         }
  332         if (verbose && i == odinfo->obscols - 1 && odinfo->S != NULL) {
  333             /* finished composing obs string, report it */
  334             pprintf(prn, " (obs '%s')", odinfo->S[t]);
  335         }
  336         } else {
  337         /* now looking for actual data */
  338         if (colbytes[i] == SQL_NULL_DATA) {
  339             if (verbose) {
  340             pputs(prn, "no data");
  341             }
  342             odinfo->X[v][t] = NADBL;
  343         } else if (strvals != NULL && strvals[v] != NULL) {
  344             odinfo->X[v][t] = strval_to_double(odinfo, strvals[v],
  345                                t+1, v+1, &err);
  346             if (verbose) {
  347             pprintf(prn, "string '%s' -> %g", strvals[v],
  348                 odinfo->X[v][t]);
  349             }
  350         } else if (dv != NULL && dv[v] != NULL) {
  351             odinfo->X[v][t] = date_to_double(odinfo, dv[v],
  352                              t+1, v+1, &err);
  353             if (verbose) {
  354             pprintf(prn, "date -> %g", odinfo->X[v][t]);
  355             }
  356         } else {
  357             odinfo->X[v][t] = xt[v];
  358             if (verbose) {
  359             pprintf(prn, "value %g", xt[v]);
  360             }
  361         }
  362         v++;
  363         }
  364         if (verbose && i < totcols-1) pputs(prn, "; ");
  365     } /* end loop across columns */
  366 
  367     if (verbose) {
  368         pputc(prn, '\n');
  369     }
  370     t++;
  371 
  372     /* try getting next row */
  373     ret = SQLFetch(stmt);
  374     if (ret == SQL_SUCCESS && t >= *nrows) {
  375         err = expand_catchment(odinfo, nrows);
  376     }
  377     } /* end loop across rows */
  378 
  379     if (ret != SQL_SUCCESS && ret != SQL_NO_DATA && !err) {
  380     err = E_DATA;
  381     }
  382 
  383     *obsgot = t;
  384 
  385     return err;
  386 }
  387 
  388 #define ODBC_STRSZ 16
  389 
  390 static char **allocate_string_grabbers (ODBC_info *odinfo,
  391                     int *nstrs,
  392                     int *err)
  393 {
  394     char **G = NULL;
  395     int i, n = 0;
  396 
  397     for (i=0; i<odinfo->obscols; i++) {
  398     if (odinfo->coltypes[i] == GRETL_TYPE_STRING ||
  399         odinfo->coltypes[i] == GRETL_TYPE_DATE) {
  400         n++;
  401     }
  402     }
  403 
  404     if (n > 0) {
  405     G = strings_array_new_with_length(n, ODBC_STRSZ);
  406     if (G == NULL) {
  407         *err = E_ALLOC;
  408     } else {
  409         *nstrs = n;
  410     }
  411     }
  412 
  413     return G;
  414 }
  415 
  416 /* Allocate a char *object of size @len to hold a
  417    string value provided by ODBC. If the array to hold
  418    such objects has not yet been allocated if must be
  419    be created first.
  420 */
  421 
  422 static char *get_str_bind_target (char ***pS, int len, int nv,
  423                   int j, int *err)
  424 {
  425     char *ret = NULL;
  426 
  427     if (*pS == NULL) {
  428     /* starting from scratch */
  429     *pS = strings_array_new(nv);
  430     if (*pS == NULL) {
  431         *err = E_ALLOC;
  432     }
  433     }
  434 
  435     if (*pS != NULL) {
  436     (*pS)[j] = calloc(len + 1, 1);
  437     if ((*pS)[j] == NULL) {
  438         *err = E_ALLOC;
  439     } else {
  440         ret = (*pS)[j];
  441     }
  442     }
  443 
  444     return ret;
  445 }
  446 
  447 static DATE_STRUCT *get_date_bind_target (DATE_STRUCT ***pds,
  448                       int nv, int j,
  449                       int *err)
  450 {
  451     DATE_STRUCT *ret = NULL;
  452 
  453     if (*pds == NULL) {
  454     /* starting from scratch */
  455     *pds = calloc(sizeof *pds, nv);
  456     if (*pds == NULL) {
  457         *err = E_ALLOC;
  458     }
  459     }
  460 
  461     if (*pds != NULL) {
  462     (*pds)[j] = calloc(sizeof **pds, 1);
  463     if ((*pds)[j] == NULL) {
  464         *err = E_ALLOC;
  465     } else {
  466         ret = (*pds)[j];
  467     }
  468     }
  469 
  470     return ret;
  471 }
  472 
  473 static gchar *sql_datatype_name (SQLSMALLINT dt, int *free_it)
  474 {
  475     switch (dt) {
  476     case SQL_CHAR:             return "SQL_CHAR";
  477     case SQL_DATE:             return "SQL_DATE";
  478     case SQL_NUMERIC:          return "SQL_NUMERIC";
  479     case SQL_DECIMAL:          return "SQL_DECIMAL";
  480     case SQL_INTEGER:          return "SQL_INTEGER";
  481     case SQL_SMALLINT:         return "SQL_SMALLINT";
  482     case SQL_FLOAT:            return "SQL_FLOAT";
  483     case SQL_REAL:             return "SQL_REAL";
  484     case SQL_DOUBLE:           return "SQL_DOUBLE";
  485     case SQL_VARCHAR:          return "SQL_VARCHAR";
  486     case SQL_WCHAR:            return "SQL_WCHAR";
  487     case SQL_WVARCHAR:         return "SQL_WVARCHAR";
  488     case SQL_TYPE_DATE:        return "SQL_DATE"; /* odbc bug ?? */
  489     }
  490 
  491     /* note: as of 2020-03-22 we're not supporting SQL_TIMESTAMP */
  492 
  493     if (free_it != NULL) {
  494     *free_it = 1;
  495     return g_strdup_printf("%d (?)", (int) dt);
  496     } else {
  497     fprintf(stderr, "data type %d not recognized or not supported\n", dt);
  498     return NULL;
  499     }
  500 }
  501 
  502 static gchar *sql_nullable_name (SQLSMALLINT nv, int *free_it)
  503 {
  504     if (nv == SQL_NO_NULLS) {
  505     return "SQL_NO_NULLS";
  506     } else if (nv == SQL_NULLABLE) {
  507     return "SQL_NULLABLE";
  508     } else {
  509     *free_it = 1;
  510     return g_strdup_printf("nullable %d (?)", (int) nv);
  511     }
  512 }
  513 
  514 #define IS_SQL_DATE(t) (t == SQL_DATE || t == SQL_TYPE_DATE)
  515 
  516 #define IS_SQL_STRING_TYPE(t) (t == SQL_CHAR || \
  517                    t == SQL_VARCHAR || \
  518                    t == SQL_WCHAR || \
  519                    t == SQL_WVARCHAR)
  520 
  521 static SQLSMALLINT get_col_info (SQLHSTMT stmt, int colnum,
  522                  int *len, PRN *prn, int *err)
  523 {
  524     SQLCHAR colname[128+1] = {0};
  525     SQLSMALLINT colname_len;
  526     SQLSMALLINT data_type;
  527     SQLULEN     colsize;
  528     SQLSMALLINT digits;
  529     SQLSMALLINT nullable;
  530     SQLRETURN ret;
  531 
  532     ret = SQLDescribeCol(stmt,             /* handle of stmt */
  533              colnum,           /* column number */
  534              colname,          /* where to put column name */
  535              sizeof(colname),  /* = 128+1 ... allow for nul */
  536              &colname_len,     /* location for name length */
  537              &data_type,       /* location for  <data type> */
  538              &colsize,         /* location for column size */
  539              &digits,          /* location for scale/frac precision */
  540              &nullable);       /* location for 'nullable' flag */
  541 
  542     if (OD_error(ret)) {
  543     gretl_errmsg_set("Error in SQLDescribeCol");
  544     *err = E_DATA;
  545     } else if (sql_datatype_name(data_type, NULL) == NULL) {
  546     gretl_errmsg_sprintf("col %d: unsupported data type %d", colnum,
  547                  (int) data_type);
  548     *err = E_DATA;
  549     } else if (len != NULL) {
  550     if (prn != NULL) {
  551         int free1 = 0, free2 = 0;
  552         gchar *dname = sql_datatype_name(data_type, &free1);
  553         gchar *nname = sql_nullable_name(nullable, &free2);
  554 
  555         pprintf(prn, " col %d (%s): data_type %s, size %d, digits %d, %s\n",
  556             colnum, colname, dname, (int) colsize, digits, nname);
  557         if (free1) g_free(dname);
  558         if (free2) g_free(nname);
  559     }
  560     *len = (int) colsize;
  561     }
  562 
  563     return data_type;
  564 }
  565 
  566 int gretl_odbc_get_data (ODBC_info *odinfo, gretlopt opt, PRN *inprn)
  567 {
  568     SQLHENV OD_env = NULL;    /* ODBC environment handle */
  569     SQLHDBC dbc = NULL;       /* connection handle */
  570     SQLHSTMT stmt = NULL;     /* statement handle */
  571     SQLRETURN ret;            /* return value from SQL functions */
  572     unsigned char status[10]; /* SQL status */
  573     unsigned char msg[512];
  574     SQLINTEGER OD_err;
  575     SQLSMALLINT mlen, ncols, dt;
  576     double *xt = NULL;
  577     SQLLEN *colbytes = NULL;
  578     SQLLEN sqlnrows;
  579     long grabint[ODBC_OBSCOLS];
  580     double grabx[ODBC_OBSCOLS];
  581     DATE_STRUCT grabd[ODBC_OBSCOLS];
  582     DATE_STRUCT **dv = NULL;
  583     char **grabstr = NULL;
  584     char **strvals = NULL;
  585     int *svlist = NULL;
  586     int totcols, nrows = 0;
  587     int nstrs = 0;
  588     int i, j, k, p, q;
  589     int T = 0, err = 0;
  590     PRN *prn;
  591 
  592     odinfo->X = NULL;
  593     odinfo->S = NULL;
  594     odinfo->nrows = 0;
  595     status[0] = msg[0] = 0;
  596 
  597     prn = (opt & OPT_V)? inprn : NULL;
  598 
  599     /* columns used in composing obs identifier (if any) plus
  600        actual data columns */
  601     totcols = odinfo->obscols + odinfo->nvars;
  602 
  603     xt = malloc(odinfo->nvars * sizeof *xt);
  604     if (xt == NULL) {
  605     return E_ALLOC;
  606     }
  607 
  608     colbytes = malloc(totcols * sizeof *colbytes);
  609     if (colbytes == NULL) {
  610     free(xt);
  611     return E_ALLOC;
  612     }
  613 
  614     grabstr = allocate_string_grabbers(odinfo, &nstrs, &err);
  615     if (err) {
  616     free(xt);
  617     free(colbytes);
  618     return err;
  619     }
  620 
  621     dbc = gretl_odbc_connect_to_dsn(odinfo, &OD_env, prn, &err);
  622     if (err) {
  623     free(xt);
  624     free(colbytes);
  625     strings_array_free(grabstr, nstrs);
  626     return err;
  627     }
  628 
  629     ret = SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt);
  630     if (OD_error(ret)) {
  631     gretl_errmsg_set("Error in AllocStatement");
  632     SQLGetDiagRec(SQL_HANDLE_DBC, dbc, 1, status, &OD_err,
  633               msg, 512, &mlen);
  634     gretl_errmsg_set((char *) msg);
  635     err = 1;
  636     goto bailout;
  637     }
  638 
  639     j = k = p = q = 0;
  640 
  641     /* bind auxiliary (obs) columns */
  642     for (i=0; i<odinfo->obscols; i++) {
  643     colbytes[i] = 0;
  644     if (odinfo->coltypes[i] == GRETL_TYPE_INT) {
  645         SQLBindCol(stmt, i+1, SQL_C_LONG, &grabint[j++], 0,
  646                &colbytes[i]);
  647     } else if (odinfo->coltypes[i] == GRETL_TYPE_STRING) {
  648         SQLBindCol(stmt, i+1, SQL_C_CHAR, grabstr[k++], ODBC_STRSZ,
  649                &colbytes[i]);
  650     } else if (odinfo->coltypes[i] == GRETL_TYPE_DATE) {
  651         SQLBindCol(stmt, i+1, SQL_C_TYPE_DATE, &grabd[q++], 10,
  652                &colbytes[i]);
  653     } else if (odinfo->coltypes[i] == GRETL_TYPE_DOUBLE) {
  654         SQLBindCol(stmt, i+1, SQL_C_DOUBLE, &grabx[p++], sizeof(double),
  655                &colbytes[i]);
  656     }
  657     }
  658 
  659     ret = SQLExecDirect(stmt, (SQLCHAR *) odinfo->query, SQL_NTS);
  660     if (OD_error(ret)) {
  661     gretl_errmsg_set("Error in SQLExecDirect");
  662     pprintf(prn, "failed query: '%s'\n", odinfo->query);
  663     SQLGetDiagRec(SQL_HANDLE_STMT, stmt, 1, status, &OD_err, msg,
  664               100, &mlen);
  665     if (msg[0] != 0) {
  666         gretl_errmsg_set((char *) msg);
  667     }
  668     err = 1;
  669     goto bailout;
  670     }
  671 
  672     ret = SQLNumResultCols(stmt, &ncols);
  673     if (OD_error(ret)) {
  674     gretl_errmsg_set("Error in SQLNumResultCols");
  675     err = 1;
  676     goto bailout;
  677     }
  678 
  679     pprintf(prn, "Number of columns = %d\n", (int) ncols);
  680     if (ncols != totcols) {
  681     gretl_errmsg_sprintf("ODBC: expected %d columns but got %d",
  682                  totcols, ncols);
  683     err = 1;
  684     goto bailout;
  685     } else if (ncols <= 0) {
  686     gretl_errmsg_set("Didn't get any data");
  687     err = E_DATA;
  688     goto bailout;
  689     }
  690 
  691     /* Are we going to need a string table? */
  692     j = 1;
  693     for (i=odinfo->obscols; i<ncols && !err; i++) {
  694     dt = get_col_info(stmt, i+1, NULL, prn, &err);
  695     if (!err && IS_SQL_STRING_TYPE(dt)) {
  696         pprintf(prn, "string table: adding ID %d\n", j);
  697         svlist = gretl_list_append_term(&svlist, j);
  698     }
  699     j++;
  700     }
  701     if (!err && svlist != NULL) {
  702     odinfo->gst = gretl_string_table_new(svlist);
  703     if (odinfo->gst == NULL) {
  704         err = E_ALLOC;
  705         goto bailout;
  706     }
  707     }
  708 
  709     /* show and process column info */
  710     for (i=0; i<ncols && !err; i++) {
  711     int len = 0;
  712 
  713     dt = get_col_info(stmt, i+1, &len, prn, &err);
  714     if (!err && i >= odinfo->obscols) {
  715         /* bind data columns */
  716         colbytes[i] = 0;
  717         j = i - odinfo->obscols;
  718         if (IS_SQL_STRING_TYPE(dt)) {
  719         char *sval = get_str_bind_target(&strvals, len, odinfo->nvars, j, &err);
  720 
  721         if (!err) {
  722             pprintf(prn, "  binding col %d to strvals[%d] (len = %d)\n",
  723                 i+1, j, len);
  724             SQLBindCol(stmt, i+1, SQL_C_CHAR, sval, len, &colbytes[i]);
  725         }
  726         } else if (IS_SQL_DATE(dt)) {
  727         DATE_STRUCT *ds = get_date_bind_target(&dv, odinfo->nvars, j, &err);
  728 
  729         if (!err) {
  730             pprintf(prn, "  binding col %d to dv[%d]\n", i+1, j);
  731             SQLBindCol(stmt, i+1, SQL_C_TYPE_DATE, ds, sizeof *ds,
  732                    &colbytes[i]);
  733         }
  734         } else {
  735         /* should be numerical data */
  736         pprintf(prn, "  binding col %d to xt[%d]\n", i+1, j);
  737         SQLBindCol(stmt, i+1, SQL_C_DOUBLE, &xt[j], sizeof(double),
  738                &colbytes[i]);
  739         }
  740     }
  741     }
  742 
  743     if (err) {
  744     goto bailout;
  745     }
  746 
  747     ret = SQLRowCount(stmt, &sqlnrows);
  748     if (OD_error(ret)) {
  749     gretl_errmsg_set("Error in SQLRowCount");
  750     err = 1;
  751     goto bailout;
  752     }
  753 
  754     /* Note that SQLRowCount can give nrows <= 0, even while returning
  755        SQL_SUCCESS, if the ODBC driver is too lazy to compute the
  756        number of rows in the result before actually fetching the data
  757        (e.g. Microsoft but maybe also others).
  758     */
  759 
  760     nrows = sqlnrows;
  761     pprintf(prn, "Number of rows (from SQLRowCount): %d\n", nrows);
  762 
  763     if (!err) {
  764     if (nrows <= 0) {
  765         nrows = ODBC_INIT_ROWS;
  766     }
  767     odinfo->X = doubles_array_new(odinfo->nvars, nrows);
  768     if (odinfo->X == NULL) {
  769         err = E_ALLOC;
  770     }
  771     }
  772 
  773     if (!err && odinfo->fmts != NULL) {
  774     odinfo->S = strings_array_new_with_length(nrows, OBSLEN);
  775     if (odinfo->S == NULL) {
  776         err = E_ALLOC;
  777     }
  778     }
  779 
  780     if (!err) {
  781     /* get the actual data */
  782     err = odbc_read_rows(odinfo, stmt, totcols, colbytes,
  783                  grabint, grabx, grabstr, grabd,
  784                  xt, dv, &nrows, &T, strvals, prn);
  785     }
  786 
  787  bailout:
  788 
  789     if (err) {
  790     doubles_array_free(odinfo->X, odinfo->nvars);
  791     odinfo->X = NULL;
  792     strings_array_free(odinfo->S, nrows);
  793     odinfo->S = NULL;
  794     } else {
  795     if (T < nrows && odinfo->S != NULL) {
  796         odinfo->S = strings_array_realloc_with_length(&odinfo->S,
  797                               nrows, T,
  798                               OBSLEN);
  799         if (odinfo->S == NULL) {
  800         err = E_ALLOC;
  801         }
  802     }
  803     odinfo->nrows = T;
  804     }
  805 
  806     free(xt);
  807     free(colbytes);
  808     free(svlist);
  809     strings_array_free(grabstr, nstrs);
  810     if (strvals != NULL) {
  811     strings_array_free(strvals, odinfo->nvars);
  812     }
  813     if (dv != NULL) {
  814     for (i=0; i<odinfo->nvars; i++) {
  815         free(dv[i]);
  816     }
  817     free(dv);
  818     }
  819 
  820     if (stmt != NULL) {
  821     ret = SQLFreeHandle(SQL_HANDLE_STMT, stmt);
  822     pprintf(prn, "SQLFreeHandle(SQL_HANDLE_STMT): %s\n", sql_status(ret));
  823     }
  824 
  825     ret = SQLDisconnect(dbc);
  826     pprintf(prn, "SQLDisconnect: %s\n", sql_status(ret));
  827     ret = SQLFreeHandle(SQL_HANDLE_DBC, dbc);
  828     pprintf(prn, "SQLFreeHandle(SQL_HANDLE_DBC): %s\n", sql_status(ret));
  829     ret = SQLFreeHandle(SQL_HANDLE_ENV, OD_env);
  830     pprintf(prn, "SQLFreeHandle(SQL_HANDLE_ENV): %s\n", sql_status(ret));
  831 
  832     return err;
  833 }