/* This file is part of the Q programming system.
The Q programming system is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
The Q programming system is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
#if defined (HAVE_CONFIG_H)
# include "config.h"
#endif
#ifdef _WIN32
#define STDC_HEADERS 1
#define HAVE_STRDUP 1
#define HAVE_MEMCPY 1
#define HAVE_LIMITS_H 1
#include <windows.h>
#endif
/* system headers */
#include <stdio.h>
#include <ctype.h>
/* check for standard C headers */
#if STDC_HEADERS
# include <stdlib.h>
# include <string.h>
#else
# ifndef HAVE_STRCHR
# define strchr index
# define strrchr rindex
# endif
char *strchr (), *strrchr ();
#endif
#ifdef HAVE_MALLOC_H
#include <stdlib.h>
#endif
#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <sql.h>
#include <sqlext.h>
#if (ODBCVER < 0x0300)
#error "Sorry, this module requires ODBC 3.0 or later!"
#endif
#include <gmp.h>
#include <libq.h>
#ifdef DMALLOC
#include <dmalloc.h>
#endif
MODULE(odbc)
#ifndef HAVE_STRDUP
static char *strdup(char *s)
{
char *t;
return ((t=malloc(strlen(s)+1))?strcpy(t, s):NULL);
}
#endif
#ifndef HAVE_MEMCPY
#if __GNUC__ > 1
#define memcpy(TO,FROM,COUNT) __builtin_memcpy(TO,FROM,COUNT)
#else
static void *memcpy (to, from, count)
char *to;
char *from;
int count;
{
register char *f = from;
register char *t = to;
register int i = count;
while (i-- > 0)
*t++ = *f++;
return (void*)to;
}
#endif
#endif
/* ByteStr data structure, see clib.c */
typedef struct bstr {
long size;
unsigned char *v;
} bstr_t;
/* Query parameter structure */
typedef struct {
short type; /* SQL parameter type */
short ctype; /* C parameter type */
long len; /* length or indicator */
long buflen; /* real buffer length */
long prec; /* precision */
void *ptr; /* buffer pointer */
union {
long iv; /* integer parameter */
double fv; /* floating point parameter */
char *buf; /* string or byte string parameter */
} data;
} ODBCParam;
/* ODBC handle structure */
typedef struct {
SQLHENV henv; /* environment handle */
SQLHDBC hdbc; /* connection handle */
SQLHSTMT hstmt; /* statement handle */
unsigned char exec; /* set while statement is being executed */
short *coltype; /* column types in current result set */
short cols; /* number of columns */
ODBCParam *argv; /* marked parameters */
int argc; /* number of marked parameters */
} ODBCHandle;
static int init_args(ODBCHandle *db, int argc)
{
int i;
if (!(db->argv = malloc(argc*sizeof(ODBCParam))))
return 0;
db->argc = argc;
for (i = 0; i < argc; i++) {
db->argv[i].type = SQL_UNKNOWN_TYPE;
db->argv[i].len = SQL_NULL_DATA;
}
return 1;
}
static void free_args(ODBCHandle *db)
{
if (db->argv) {
int i;
SQLFreeStmt(db->hstmt, SQL_RESET_PARAMS);
for (i = 0; i < db->argc; i++)
if ((db->argv[i].type == SQL_BIGINT || db->argv[i].type == SQL_CHAR ||
db->argv[i].type == SQL_BINARY) &&
db->argv[i].data.buf)
free(db->argv[i].data.buf);
free(db->argv);
db->argv = NULL;
db->argc = 0;
}
}
static int set_arg(ODBCHandle *db, int i, expr x)
{
long iv;
double fv;
char *s;
bstr_t *m;
mpz_t z;
if (isint(x, &iv)) {
db->argv[i].type = SQL_INTEGER;
db->argv[i].ctype = SQL_C_SLONG;
db->argv[i].len = sizeof(long);
db->argv[i].buflen = sizeof(long);
db->argv[i].prec = 10;
db->argv[i].data.iv = iv;
db->argv[i].ptr = &db->argv[i].data.iv;
return 1;
} else if (ismpz(x, z)) {
/* convert big integer values to BIGINTs via a string representation,
so we don't have to fiddle with long long's here */
db->argv[i].type = SQL_BIGINT;
db->argv[i].ctype = SQL_C_CHAR;
db->argv[i].len = SQL_NTS;
db->argv[i].data.buf = mpz_get_str(NULL, 10, z);
if (!db->argv[i].data.buf) return 0;
db->argv[i].buflen = strlen(db->argv[i].data.buf)+1;
db->argv[i].prec = db->argv[i].buflen-1;
db->argv[i].ptr = db->argv[i].data.buf;
return 1;
} else if (isfloat(x, &fv)) {
db->argv[i].type = SQL_DOUBLE;
db->argv[i].ctype = SQL_C_DOUBLE;
db->argv[i].len = sizeof(double);
db->argv[i].buflen = sizeof(double);
db->argv[i].prec = 15;
db->argv[i].data.fv = fv;
db->argv[i].ptr = &db->argv[i].data.fv;
return 1;
} else if (isstr(x, &s)) {
db->argv[i].type = SQL_CHAR;
db->argv[i].ctype = SQL_C_CHAR;
db->argv[i].len = SQL_NTS;
db->argv[i].buflen = strlen(s)+1;
db->argv[i].prec = db->argv[i].buflen-1;
db->argv[i].data.buf = strdup(s);
db->argv[i].ptr = db->argv[i].data.buf;
return db->argv[i].data.buf != NULL;
} else if (isobj(x, type(ByteStr), (void**)&m)) {
db->argv[i].type = SQL_BINARY;
db->argv[i].ctype = SQL_C_BINARY;
db->argv[i].len = m->size;
db->argv[i].buflen = m->size;
db->argv[i].prec = m->size;
if (m->size > 0) {
if (!(db->argv[i].data.buf = malloc(m->size)))
return 0;
memcpy(db->argv[i].data.buf, m->v, m->size);
} else
db->argv[i].data.buf = NULL;
db->argv[i].ptr = db->argv[i].data.buf;
return 1;
} else if (isvoid(x)) {
db->argv[i].type = SQL_CHAR;
db->argv[i].ctype = SQL_C_DEFAULT;
db->argv[i].len = SQL_NULL_DATA;
db->argv[i].buflen = 0;
db->argv[i].prec = 0;
db->argv[i].data.buf = NULL;
db->argv[i].ptr = NULL;
return 1;
} else
return 0;
}
static void sql_close(ODBCHandle *db)
{
if (db->exec) {
if (db->coltype) free(db->coltype);
free_args(db);
SQLFreeStmt(db->hstmt, SQL_CLOSE);
db->coltype = NULL;
db->cols = 0;
db->exec = 0;
}
}
DESTRUCTOR(odbc,ODBCHandle,ptr)
{
ODBCHandle *db = (ODBCHandle*)ptr;
if (db->henv) {
sql_close(db);
SQLCloseCursor(db->hstmt);
SQLFreeHandle(SQL_HANDLE_STMT, db->hstmt);
SQLDisconnect(db->hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, db->hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, db->henv);
}
free(db);
}
static expr mkerr(SQLHENV henv, SQLHDBC hdbc, SQLHSTMT hstmt)
{
char stat[10], msg[300];
SQLINTEGER err;
short len;
/* check for SQL statement errors */
if (hstmt && SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, stat, &err,
msg, sizeof(msg), &len) == SQL_SUCCESS)
goto exit;
/* check for connection errors */
if (hdbc && SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, 1, stat, &err,
msg, sizeof(msg), &len) == SQL_SUCCESS)
goto exit;
/* check for environment errors */
if (henv && SQLGetDiagRec(SQL_HANDLE_ENV, henv, 1, stat, &err,
msg, sizeof(msg), &len) == SQL_SUCCESS)
goto exit;
return __FAIL;
exit:
return mkapp(mkapp(mksym(sym(odbc_error)), mkstr(strdup(msg))),
mkstr(strdup(stat)));
}
FUNCTION(odbc,odbc_sources,argc,argv)
{
if (argc == 0) {
SQLHENV henv;
long ret;
expr *xv;
int n;
char l_dsn[100],l_desc[100];
short l_len1, l_len2, l_next;
/* create an environment handle */
if ((ret = SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv)) != SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO)
return __FAIL;
if ((ret = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER) SQL_OV_ODBC3,
SQL_IS_UINTEGER)) != SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO) {
expr msg = mkerr(henv, 0, 0);
SQLFreeHandle(SQL_HANDLE_ENV, henv);
return msg;
}
/* count the number of data sources */
for (n = 0, l_next = SQL_FETCH_FIRST;
SQLDataSources(henv, l_next, l_dsn, sizeof(l_dsn), &l_len1,
l_desc, sizeof(l_desc), &l_len2) == SQL_SUCCESS;
l_next = SQL_FETCH_NEXT)
n++;
if (!(xv = malloc(n*sizeof(expr)))) {
SQLFreeHandle(SQL_HANDLE_ENV, henv);
return __ERROR;
}
/* retrieve the data source names and descriptions */
for (n = 0, l_next = SQL_FETCH_FIRST;
SQLDataSources(henv, l_next, l_dsn, sizeof(l_dsn), &l_len1,
l_desc, sizeof(l_desc), &l_len2) == SQL_SUCCESS;
l_next = SQL_FETCH_NEXT)
xv[n++] = mktuplel(2, mkstr(strdup(l_dsn)), mkstr(strdup(l_desc)));
/* free the environment handle */
SQLFreeHandle(SQL_HANDLE_ENV, henv);
return mklistv(n, xv);
} else
return __FAIL;
}
FUNCTION(odbc,odbc_drivers,argc,argv)
{
if (argc == 0) {
SQLHENV henv;
long ret;
expr *xv;
int n;
char l_drv[100],l_attr[10000];
short l_len1, l_len2, l_next;
/* create an environment handle */
if ((ret = SQLAllocHandle(SQL_HANDLE_ENV, NULL, &henv)) != SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO)
return __FAIL;
if ((ret = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER) SQL_OV_ODBC3,
SQL_IS_UINTEGER)) != SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO) {
expr msg = mkerr(henv, 0, 0);
SQLFreeHandle(SQL_HANDLE_ENV, henv);
return msg;
}
/* count the number of driver descriptions */
for (n = 0, l_next = SQL_FETCH_FIRST;
SQLDrivers(henv, l_next, l_drv, sizeof(l_drv), &l_len1,
l_attr, sizeof(l_attr), &l_len2) == SQL_SUCCESS;
l_next = SQL_FETCH_NEXT)
n++;
if (!(xv = malloc(n*sizeof(expr)))) {
SQLFreeHandle(SQL_HANDLE_ENV, henv);
return __ERROR;
}
/* retrieve the driver and descriptions */
for (n = 0, l_next = SQL_FETCH_FIRST;
SQLDrivers(henv, l_next, l_drv, sizeof(l_drv), &l_len1,
l_attr, sizeof(l_attr), &l_len2) == SQL_SUCCESS;
l_next = SQL_FETCH_NEXT) {
int k;
char *l_attrp;
expr *yv;
/* count the number of attributes */
for (k = 0, l_attrp = l_attr; *l_attrp;
l_attrp = l_attrp+strlen(l_attrp)+1)
k++;
if (!(yv = malloc(k*sizeof(expr)))) {
int i;
for (i = 0; i < n; i++)
dispose(xv[i]);
free(xv);
SQLFreeHandle(SQL_HANDLE_ENV, henv);
return __ERROR;
}
/* get the attribute strings */
for (k = 0, l_attrp = l_attr; *l_attrp;
l_attrp = l_attrp+strlen(l_attrp)+1)
yv[k++] = mkstr(strdup(l_attrp));
xv[n++] = mktuplel(2, mkstr(strdup(l_drv)), mklistv(k, yv));
}
/* free the environment handle */
SQLFreeHandle(SQL_HANDLE_ENV, henv);
return mklistv(n, xv);
} else
return __FAIL;
}
FUNCTION(odbc,odbc_connect,argc,argv)
{
char *conn;
if (argc == 1 && isstr(argv[0], &conn)) {
ODBCHandle *db = (ODBCHandle*)malloc(sizeof(ODBCHandle));
long ret;
short buflen;
char buf[1024];
if (!db) return __ERROR;
/* create the environment handle */
if ((ret = SQLAllocHandle(SQL_HANDLE_ENV, NULL, &db->henv)) !=
SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO)
return __FAIL;
if ((ret = SQLSetEnvAttr(db->henv, SQL_ATTR_ODBC_VERSION,
(SQLPOINTER) SQL_OV_ODBC3,
SQL_IS_UINTEGER)) != SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO) {
expr msg = mkerr(db->henv, 0, 0);
SQLFreeHandle(SQL_HANDLE_ENV, db->henv);
return msg;
}
/* create the connection handle */
if ((ret = SQLAllocHandle(SQL_HANDLE_DBC, db->henv, &db->hdbc)) !=
SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO) {
expr msg = mkerr(db->henv, 0, 0);
SQLFreeHandle(SQL_HANDLE_ENV, db->henv);
return msg;
}
/* connect */
if ((ret = SQLDriverConnect(db->hdbc, 0, conn, SQL_NTS,
buf, sizeof(buf), &buflen,
SQL_DRIVER_NOPROMPT)) != SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO) {
expr msg = mkerr(db->henv, db->hdbc, 0);
SQLFreeHandle(SQL_HANDLE_DBC, db->hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, db->henv);
return msg;
}
/* create the statement handle */
if ((ret = SQLAllocHandle(SQL_HANDLE_STMT, db->hdbc, &db->hstmt)) !=
SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO) {
expr msg = mkerr(db->henv, db->hdbc, 0);
SQLDisconnect(db->hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, db->hdbc);
SQLFreeHandle(SQL_HANDLE_ENV, db->henv);
return msg;
}
/* initialize statement properties */
db->argv = NULL;
db->argc = 0;
db->coltype = NULL;
db->cols = 0;
db->exec = 0;
/* return the result */
return mkobj(type(ODBCHandle), db);
} else
return __FAIL;
}
FUNCTION(odbc,odbc_disconnect,argc,argv)
{
ODBCHandle *db;
if (argc == 1 && isobj(argv[0], type(ODBCHandle), (void**)&db) &&
db->henv) {
sql_close(db);
SQLCloseCursor(db->hstmt);
SQLFreeHandle(SQL_HANDLE_STMT, db->hstmt);
db->hstmt = 0;
SQLDisconnect(db->hdbc);
SQLFreeHandle(SQL_HANDLE_DBC, db->hdbc);
db->hdbc = 0;
SQLFreeHandle(SQL_HANDLE_ENV, db->henv);
db->henv = 0;
return mkvoid;
} else
return __FAIL;
}
FUNCTION(odbc,odbc_info,argc,argv)
{
ODBCHandle *db;
if (argc == 1 && isobj(argv[0], type(ODBCHandle), (void**)&db) &&
db->henv) {
long ret;
int n = 0;
expr *xv = malloc(8*sizeof(expr));
char info[1024];
short len;
if (!xv) return __ERROR;
if ((ret = SQLGetInfo(db->hdbc, SQL_DATA_SOURCE_NAME,
info, sizeof(info), &len)) == SQL_SUCCESS ||
ret == SQL_SUCCESS_WITH_INFO)
xv[n++] = mkstr(strdup(info));
else
xv[n++] = mkstr(strdup(""));
if ((ret = SQLGetInfo(db->hdbc, SQL_DATABASE_NAME,
info, sizeof(info), &len)) == SQL_SUCCESS ||
ret == SQL_SUCCESS_WITH_INFO)
xv[n++] = mkstr(strdup(info));
else
xv[n++] = mkstr(strdup(""));
if ((ret = SQLGetInfo(db->hdbc, SQL_DBMS_NAME,
info, sizeof(info), &len)) == SQL_SUCCESS ||
ret == SQL_SUCCESS_WITH_INFO)
xv[n++] = mkstr(strdup(info));
else
xv[n++] = mkstr(strdup(""));
if ((ret = SQLGetInfo(db->hdbc, SQL_DBMS_VER,
info, sizeof(info), &len)) == SQL_SUCCESS ||
ret == SQL_SUCCESS_WITH_INFO)
xv[n++] = mkstr(strdup(info));
else
xv[n++] = mkstr(strdup(""));
if ((ret = SQLGetInfo(db->hdbc, SQL_DRIVER_NAME,
info, sizeof(info), &len)) == SQL_SUCCESS ||
ret == SQL_SUCCESS_WITH_INFO)
xv[n++] = mkstr(strdup(info));
else
xv[n++] = mkstr(strdup(""));
if ((ret = SQLGetInfo(db->hdbc, SQL_DRIVER_VER,
info, sizeof(info), &len)) == SQL_SUCCESS ||
ret == SQL_SUCCESS_WITH_INFO)
xv[n++] = mkstr(strdup(info));
else
xv[n++] = mkstr(strdup(""));
if ((ret = SQLGetInfo(db->hdbc, SQL_DRIVER_ODBC_VER,
info, sizeof(info), &len)) == SQL_SUCCESS ||
ret == SQL_SUCCESS_WITH_INFO)
xv[n++] = mkstr(strdup(info));
else
xv[n++] = mkstr(strdup(""));
if ((ret = SQLGetInfo(db->hdbc, SQL_ODBC_VER,
info, sizeof(info), &len)) == SQL_SUCCESS ||
ret == SQL_SUCCESS_WITH_INFO)
xv[n++] = mkstr(strdup(info));
else
xv[n++] = mkstr(strdup(""));
return mktuplev(n, xv);
} else
return __FAIL;
}
#define BUFSZ 65536
#define BUFSZ2 5000
FUNCTION(odbc,sql_exec,argc,argv)
{
ODBCHandle *db;
char *query;
if (argc == 3 && isobj(argv[0], type(ODBCHandle), (void**)&db) &&
db->henv && isstr(argv[1], &query)) {
long ret;
expr res, *xs, *xv;
int n;
short i, cols, *coltype = NULL;
char buf[BUFSZ2];
/* finalize previous query */
sql_close(db);
/* prepare statement */
if ((ret = SQLPrepare(db->hstmt, query, SQL_NTS)) != SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO)
return mkerr(db->henv, db->hdbc, db->hstmt);
/* bind parameters */
if (isvoid(argv[2]))
;
else if (istuple(argv[2], &n, &xv)) {
if (!init_args(db, n))
goto fatal;
for (i = 0; i < n; i++)
if (!set_arg(db, i, xv[i])) {
int alloc_error =
(db->argv[i].type == SQL_BIGINT ||
db->argv[i].type == SQL_CHAR ||
db->argv[i].type == SQL_BINARY) &&
!db->argv[i].data.buf;
free_args(db);
if (alloc_error)
goto fatal;
else
goto fail;
}
} else {
if (!init_args(db, 1))
goto fatal;
if (!set_arg(db, 0, argv[2])) {
int alloc_error =
(db->argv[0].type == SQL_BIGINT ||
db->argv[0].type == SQL_CHAR ||
db->argv[0].type == SQL_BINARY) &&
!db->argv[0].data.buf;
free_args(db);
if (alloc_error)
goto fatal;
else
goto fail;
}
}
for (i = 0; i < db->argc; i++)
if ((ret = SQLBindParameter(db->hstmt, i+1, SQL_PARAM_INPUT,
db->argv[i].ctype,
db->argv[i].type,
db->argv[i].prec, 0,
db->argv[i].ptr,
db->argv[i].buflen,
&db->argv[i].len)) != SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO)
goto err;
/* execute statement */
if ((ret = SQLExecute(db->hstmt)) != SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO)
return mkerr(db->henv, db->hdbc, db->hstmt);
/* determine the number of columns */
if ((ret = SQLNumResultCols(db->hstmt, &cols)) != SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO)
goto err;
if (cols == 0) {
SQLINTEGER rows;
if ((ret = SQLRowCount(db->hstmt, &rows)) == SQL_SUCCESS ||
ret == SQL_SUCCESS_WITH_INFO)
res = mkint((long)rows);
else
res = mkint(0);
db->exec = 1;
goto exit;
}
/* get the column names and types */
if (!(coltype = malloc(cols*sizeof(short))))
goto fatal;
if (!(xs = malloc(cols*sizeof(expr))))
goto fatal;
for (i = 0; i < cols; i++) {
buf[0] = 0;
if ((ret = SQLDescribeCol(db->hstmt, i+1, buf, sizeof(buf), NULL,
&coltype[i], NULL, NULL, NULL))
!= SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO) {
int j;
for (j = 0; j < i; j++) dispose(xs[j]);
free(xs);
goto err;
}
xs[i] = mkstr(strdup(buf));
}
res = mktuplev(cols, xs);
if (res) {
db->coltype = coltype;
db->cols = cols;
coltype = NULL;
db->exec = 1;
} else {
free_args(db);
SQLFreeStmt(db->hstmt, SQL_CLOSE);
}
goto exit;
fail:
free_args(db);
SQLFreeStmt(db->hstmt, SQL_CLOSE);
res = __FAIL;
goto exit;
err:
free_args(db);
SQLFreeStmt(db->hstmt, SQL_CLOSE);
res = mkerr(db->henv, db->hdbc, db->hstmt);
goto exit;
fatal:
free_args(db);
SQLFreeStmt(db->hstmt, SQL_CLOSE);
res = __ERROR;
exit:
if (coltype) free(coltype);
return res;
} else
return __FAIL;
}
FUNCTION(odbc,sql_fetch,argc,argv)
{
ODBCHandle *db;
if (argc == 1 && isobj(argv[0], type(ODBCHandle), (void**)&db) &&
db->henv && db->coltype) {
long ret;
expr res, *xs;
short i, j, cols = db->cols, *coltype = db->coltype;
long iv, sz = BUFSZ;
double fv;
char *buf = malloc(sz);
SDWORD len;
if (!buf) goto fatal;
/* fetch the next record */
if ((ret = SQLFetch(db->hstmt)) == SQL_NO_DATA_FOUND) {
res = __FAIL;
goto exit;
} else if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO)
goto err;
if (!(xs = malloc(cols*sizeof(expr))))
goto fatal;
/* get the columns */
for (i = 0; i < cols; i++) {
switch (coltype[i]) {
case SQL_BIT:
case SQL_TINYINT:
case SQL_SMALLINT:
case SQL_INTEGER:
if ((ret = SQLGetData(db->hstmt, i+1, SQL_INTEGER, &iv,
sizeof(iv), &len) != SQL_SUCCESS) &&
ret != SQL_SUCCESS_WITH_INFO)
goto err2;
if (len == SQL_NULL_DATA)
xs[i] = mkvoid;
else
xs[i] = mkint(iv);
break;
case SQL_BIGINT:
/* hack to get bigint values converted to mpz_t, without having to
fiddle around with long long values
XXXFIXME: we should really avoid the string conversion here */
if ((ret = SQLGetData(db->hstmt, i+1, SQL_CHAR, buf,
sz, &len) != SQL_SUCCESS) &&
ret != SQL_SUCCESS_WITH_INFO)
goto err2;
if (len == SQL_NULL_DATA)
xs[i] = mkvoid;
else {
mpz_t z;
mpz_init(z);
mpz_set_str(z, buf, 0);
xs[i] = mkmpz(z);
}
break;
case SQL_DOUBLE:
case SQL_DECIMAL:
case SQL_NUMERIC:
case SQL_FLOAT:
case SQL_REAL:
if ((ret = SQLGetData(db->hstmt, i+1, SQL_DOUBLE, &fv,
sizeof(fv), &len) != SQL_SUCCESS) &&
ret != SQL_SUCCESS_WITH_INFO)
goto err2;
if (len == SQL_NULL_DATA)
xs[i] = mkvoid;
else
xs[i] = mkfloat(fv);
break;
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY: {
char *bufp = buf;
long total = 0, actsz = sz;
*buf = 0;
while (1) {
if ((ret = SQLGetData(db->hstmt, i+1, SQL_BINARY, bufp,
actsz, &len)) == SQL_SUCCESS ||
ret == SQL_NO_DATA) {
if (len == SQL_NULL_DATA)
break;
if (INT_MAX - len <= total)
goto fatal2;
else
total += len;
break;
} else if (ret == SQL_SUCCESS_WITH_INFO) {
/* we probably need to make room for additional data */
char *buf1;
if (len == SQL_NULL_DATA)
break;
if (INT_MAX - BUFSZ <= total)
goto fatal2;
else
total += actsz;
if (!(buf1 = realloc(buf, sz+BUFSZ)))
goto fatal2;
buf = buf1;
bufp = buf+total;
sz += BUFSZ;
actsz = BUFSZ;
} else {
/* some other error, bail out */
goto err2;
}
}
if (len == SQL_NULL_DATA)
xs[i] = mkvoid;
else if (total == 0) {
bstr_t *m;
if (!(m = malloc(sizeof(bstr_t))))
goto fatal2;
m->size = 0;
m->v = NULL;
xs[i] = mkobj(type(ByteStr), m);
} else {
char *buf1 = realloc(buf, total);
bstr_t *m;
if (buf1) buf = buf1;
if (!(m = malloc(sizeof(bstr_t))))
goto fatal2;
m->size = total;
m->v = buf;
xs[i] = mkobj(type(ByteStr), m);
/* make a new buffer */
if (!(buf = malloc(BUFSZ)))
goto fatal2;
else
sz = BUFSZ;
}
break;
}
default: {
char *bufp = buf;
long total = 0, actsz = sz;
*buf = 0;
while (1) {
if ((ret = SQLGetData(db->hstmt, i+1, SQL_CHAR, bufp,
actsz, &len)) == SQL_SUCCESS ||
ret == SQL_NO_DATA) {
if (len == SQL_NULL_DATA)
break;
if (INT_MAX - len <= total)
goto fatal2;
else
total += len;
break;
} else if (ret == SQL_SUCCESS_WITH_INFO) {
/* we probably need to make room for additional data */
char *buf1;
if (len == SQL_NULL_DATA)
break;
if (INT_MAX - BUFSZ <= total)
goto fatal2;
else
total += actsz-1;
if (!(buf1 = realloc(buf, sz+BUFSZ)))
goto fatal2;
buf = buf1;
bufp = buf+total;
sz += BUFSZ;
actsz = BUFSZ+1;
} else {
/* some other error, bail out */
goto err2;
}
}
if (len == SQL_NULL_DATA)
xs[i] = mkvoid;
else if (sz == BUFSZ)
xs[i] = mkstr(strdup(buf));
else {
/* avoid copying the (potentially large) buffer, instead make a new
one */
char *buf1 = realloc(buf, total);
if (buf1) buf = buf1;
xs[i] = mkstr(buf);
if (!(buf = malloc(BUFSZ)))
goto fatal2;
else
sz = BUFSZ;
}
}
}
}
res = mktuplev(cols, xs);
goto exit;
err2:
for (j = 0; j < i; j++) dispose(xs[j]);
free(xs);
err:
res = mkerr(db->henv, db->hdbc, db->hstmt);
goto exit;
fatal2:
for (j = 0; j < i; j++) dispose(xs[j]);
free(xs);
fatal:
res = __ERROR;
exit:
if (buf) free(buf);
return res;
} else
return __FAIL;
}
FUNCTION(odbc,sql_more,argc,argv)
{
ODBCHandle *db;
if (argc == 1 && isobj(argv[0], type(ODBCHandle), (void**)&db) &&
db->henv && db->exec) {
long ret;
expr res, *xs;
short i, cols, *coltype = NULL;
char buf[BUFSZ2];
SDWORD len;
/* get the next result set */
if ((ret = SQLMoreResults(db->hstmt)) == SQL_NO_DATA_FOUND) {
res = __FAIL;
goto exit;
} else if (ret != SQL_SUCCESS && ret != SQL_SUCCESS_WITH_INFO)
goto err;
/* determine the number of columns */
if ((ret = SQLNumResultCols(db->hstmt, &cols)) != SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO)
goto err;
if (cols == 0) {
SQLINTEGER rows;
if ((ret = SQLRowCount(db->hstmt, &rows)) == SQL_SUCCESS ||
ret == SQL_SUCCESS_WITH_INFO)
res = mkint((long)rows);
else
res = mkint(0);
if (db->coltype) free(db->coltype);
db->coltype = NULL;
db->cols = 0;
goto exit;
}
/* get the column names and types */
if (!(coltype = malloc(cols*sizeof(short))))
goto fatal;
if (!(xs = malloc(cols*sizeof(expr))))
goto fatal;
for (i = 0; i < cols; i++) {
buf[0] = 0;
if ((ret = SQLDescribeCol(db->hstmt, i+1, buf, sizeof(buf), NULL,
&coltype[i], NULL, NULL, NULL))
!= SQL_SUCCESS &&
ret != SQL_SUCCESS_WITH_INFO) {
int j;
for (j = 0; j < i; j++) dispose(xs[j]);
free(xs);
goto err;
}
xs[i] = mkstr(strdup(buf));
}
res = mktuplev(cols, xs);
if (res) {
free(db->coltype);
if (db->coltype) db->coltype = coltype;
db->cols = cols;
coltype = NULL;
}
goto exit;
err:
res = mkerr(db->henv, db->hdbc, db->hstmt);
goto exit;
fatal:
res = __ERROR;
exit:
if (coltype) free(coltype);
return res;
} else
return __FAIL;
}
FUNCTION(odbc,sql_close,argc,argv)
{
ODBCHandle *db;
if (argc == 1 && isobj(argv[0], type(ODBCHandle), (void**)&db) &&
db->henv && db->exec) {
sql_close(db);
return mkvoid;
} else
return __FAIL;
}
syntax highlighted by Code2HTML, v. 0.9.1