libdb/lang/sql/odbc/sqliteodbc.c
2011-09-13 13:44:24 -04:00

15762 lines
371 KiB
C

/**
* @file sqliteodbc.c
* SQLite ODBC Driver main module.
*
* $Id: sqliteodbc.c,v 1.170 2010/05/25 14:19:04 chw Exp chw $
*
* Copyright (c) 2001-2010 Christian Werner <chw@ch-werner.de>
* OS/2 Port Copyright (c) 2004 Lorne R. Sunley <lsunley@mb.sympatico.ca>
*
* See the file "license.terms" for information on usage
* and redistribution of this file and for a
* DISCLAIMER OF ALL WARRANTIES.
*/
#include "sqliteodbc.h"
#undef WINTERFACE
#ifdef SQLITE_UTF8
#ifndef WITHOUT_WINTERFACE
#define WINTERFACE
#endif
#endif
#ifdef WINTERFACE
#include <sqlucode.h>
#endif
#if defined(_WIN32) || defined(_WIN64)
#include "resource.h"
#define ODBC_INI "ODBC.INI"
#ifndef DRIVER_VER_INFO
#define DRIVER_VER_INFO VERSION
#endif
#else
#ifdef __OS2__
#define ODBC_INI "ODBC.INI"
#else
#define ODBC_INI ".odbc.ini"
#endif
#endif
#ifdef _WIN64
#define CANT_PASS_VALIST_AS_CHARPTR
#endif
#ifdef CANT_PASS_VALIST_AS_CHARPTR
#define MAX_PARAMS_FOR_VPRINTF 32
#endif
#ifndef DRIVER_VER_INFO
#define DRIVER_VER_INFO "0.0"
#endif
#ifndef COLATTRIBUTE_LAST_ARG_TYPE
#ifdef _WIN64
#define COLATTRIBUTE_LAST_ARG_TYPE SQLLEN *
#else
#define COLATTRIBUTE_LAST_ARG_TYPE SQLPOINTER
#endif
#endif
#ifndef SETSTMTOPTION_LAST_ARG_TYPE
#define SETSTMTOPTION_LAST_ARG_TYPE SQLROWCOUNT
#endif
#undef min
#define min(a, b) ((a) < (b) ? (a) : (b))
#undef max
#define max(a, b) ((a) < (b) ? (b) : (a))
#ifndef PTRDIFF_T
#define PTRDIFF_T int
#endif
#define array_size(x) (sizeof (x) / sizeof (x[0]))
#define stringify1(s) #s
#define stringify(s) stringify1(s)
#define verinfo(maj, min, lev) ((maj) << 16 | (min) << 8 | (lev))
/* Column types for static string column descriptions (SQLTables etc.) */
#if defined(WINTERFACE) && !defined(_WIN32) && !defined(_WIN64)
#define SCOL_VARCHAR SQL_WVARCHAR
#define SCOL_CHAR SQL_WCHAR
#else
#define SCOL_VARCHAR SQL_VARCHAR
#define SCOL_CHAR SQL_CHAR
#endif
#define ENV_MAGIC 0x53544145
#define DBC_MAGIC 0x53544144
#define DEAD_MAGIC 0xdeadbeef
#ifdef MEMORY_DEBUG
static void *
xmalloc_(int n, char *file, int line)
{
int nn = n + 4 * sizeof (long);
long *p;
p = malloc(nn);
if (!p) {
#if (MEMORY_DEBUG > 1)
fprintf(stderr, "malloc\t%d\tNULL\t%s:%d\n", n, file, line);
#endif
return NULL;
}
p[0] = 0xdead1234;
nn = nn / sizeof (long) - 1;
p[1] = n;
p[nn] = 0xdead5678;
#if (MEMORY_DEBUG > 1)
fprintf(stderr, "malloc\t%d\t%p\t%s:%d\n", n, &p[2], file, line);
#endif
return (void *) &p[2];
}
static void *
xrealloc_(void *old, int n, char *file, int line)
{
int nn = n + 4 * sizeof (long), nnn;
long *p, *pp;
if (n == 0 || !old) {
return xmalloc_(n, file, line);
}
p = &((long *) old)[-2];
if (p[0] != 0xdead1234) {
fprintf(stderr, "*** low end corruption @ %p\n", old);
abort();
}
nnn = p[1] + 4 * sizeof (long);
nnn = nnn / sizeof (long) - 1;
if (p[nnn] != 0xdead5678) {
fprintf(stderr, "*** high end corruption @ %p\n", old);
abort();
}
pp = realloc(p, nn);
if (!pp) {
#if (MEMORY_DEBUG > 1)
fprintf(stderr, "realloc\t%p,%d\tNULL\t%s:%d\n", old, n, file, line);
#endif
return NULL;
}
#if (MEMORY_DEBUG > 1)
fprintf(stderr, "realloc\t%p,%d\t%p\t%s:%d\n", old, n, &pp[2], file, line);
#endif
p = pp;
p[1] = n;
nn = nn / sizeof (long) - 1;
p[nn] = 0xdead5678;
return (void *) &p[2];
}
static void
xfree_(void *x, char *file, int line)
{
long *p;
int n;
if (!x) {
return;
}
p = &((long *) x)[-2];
if (p[0] != 0xdead1234) {
fprintf(stderr, "*** low end corruption @ %p\n", x);
abort();
}
n = p[1] + 4 * sizeof (long);
n = n / sizeof (long) - 1;
if (p[n] != 0xdead5678) {
fprintf(stderr, "*** high end corruption @ %p\n", x);
abort();
}
#if (MEMORY_DEBUG > 1)
fprintf(stderr, "free\t%p\t\t%s:%d\n", x, file, line);
#endif
free(p);
}
static void
xfree__(void *x)
{
xfree_(x, "unknown location", 0);
}
static char *
xstrdup_(const char *str, char *file, int line)
{
char *p;
if (!str) {
#if (MEMORY_DEBUG > 1)
fprintf(stderr, "strdup\tNULL\tNULL\t%s:%d\n", file, line);
#endif
return NULL;
}
p = xmalloc_(strlen(str) + 1, file, line);
if (p) {
strcpy(p, str);
}
#if (MEMORY_DEBUG > 1)
fprintf(stderr, "strdup\t%p\t%p\t%s:%d\n", str, p, file, line);
#endif
return p;
}
#define xmalloc(x) xmalloc_(x, __FILE__, __LINE__)
#define xrealloc(x,y) xrealloc_(x, y, __FILE__, __LINE__)
#define xfree(x) xfree_(x, __FILE__, __LINE__)
#define xstrdup(x) xstrdup_(x, __FILE__, __LINE__)
#else
#define xmalloc(x) malloc(x)
#define xrealloc(x,y) realloc(x, y)
#define xfree(x) free(x)
#define xstrdup(x) strdup_(x)
#endif
#if defined(_WIN32) || defined(_WIN64)
#define vsnprintf _vsnprintf
#define snprintf _snprintf
#define strcasecmp _stricmp
#define strncasecmp _strnicmp
static HINSTANCE NEAR hModule; /* Saved module handle for resources */
#endif
#if defined(_WIN32) || defined(_WIN64)
/*
* SQLHENV, SQLHDBC, and SQLHSTMT synchronization
* is done using a critical section in ENV structure.
*/
#define HDBC_LOCK(hdbc) \
{ \
DBC *d; \
\
if ((hdbc) == SQL_NULL_HDBC) { \
return SQL_INVALID_HANDLE; \
} \
d = (DBC *) (hdbc); \
if (d->magic != DBC_MAGIC || !d->env) { \
return SQL_INVALID_HANDLE; \
} \
if (d->env->magic != ENV_MAGIC) { \
return SQL_INVALID_HANDLE; \
} \
EnterCriticalSection(&d->env->cs); \
d->env->owner = GetCurrentThreadId(); \
}
#define HDBC_UNLOCK(hdbc) \
if ((hdbc) != SQL_NULL_HDBC) { \
DBC *d; \
\
d = (DBC *) (hdbc); \
if (d->magic == DBC_MAGIC && d->env && \
d->env->magic == ENV_MAGIC) { \
d->env->owner = 0; \
LeaveCriticalSection(&d->env->cs); \
} \
}
#define HSTMT_LOCK(hstmt) \
{ \
DBC *d; \
\
if ((hstmt) == SQL_NULL_HSTMT) { \
return SQL_INVALID_HANDLE; \
} \
d = (DBC *) ((STMT *) (hstmt))->dbc; \
if (d->magic != DBC_MAGIC || !d->env) { \
return SQL_INVALID_HANDLE; \
} \
if (d->env->magic != ENV_MAGIC) { \
return SQL_INVALID_HANDLE; \
} \
EnterCriticalSection(&d->env->cs); \
d->env->owner = GetCurrentThreadId(); \
}
#define HSTMT_UNLOCK(hstmt) \
if ((hstmt) != SQL_NULL_HSTMT) { \
DBC *d; \
\
d = (DBC *) ((STMT *) (hstmt))->dbc; \
if (d->magic == DBC_MAGIC && d->env && \
d->env->magic == ENV_MAGIC) { \
d->env->owner = 0; \
LeaveCriticalSection(&d->env->cs); \
} \
}
#else
/*
* On UN*X assume that we are single-threaded or
* the driver manager provides serialization for us.
*
* In iODBC (3.52.x) serialization can be turned
* on using the DSN property "ThreadManager=yes".
*
* In unixODBC that property is named
* "Threading=0-3" and takes one of these values:
*
* 0 - no protection
* 1 - statement level protection
* 2 - connection level protection
* 3 - environment level protection
*
* unixODBC 2.2.11 uses environment level protection
* by default when it has been built with pthread
* support.
*/
#define HDBC_LOCK(hdbc)
#define HDBC_UNLOCK(hdbc)
#define HSTMT_LOCK(hdbc)
#define HSTMT_UNLOCK(hdbc)
#endif
/*
* tolower() replacement w/o locale
*/
static const char upper_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static const char lower_chars[] = "abcdefghijklmnopqrstuvwxyz";
static int
TOLOWER(int c)
{
if (c) {
char *p = strchr(upper_chars, c);
if (p) {
c = lower_chars[p - upper_chars];
}
}
return c;
}
/*
* isdigit() replacement w/o ctype.h
*/
static const char digit_chars[] = "0123456789";
#define ISDIGIT(c) \
((c) && strchr(digit_chars, (c)) != NULL)
/*
* isspace() replacement w/o ctype.h
*/
static const char space_chars[] = " \f\n\r\t\v";
#define ISSPACE(c) \
((c) && strchr(space_chars, (c)) != NULL)
/*
* Characters in named parameters
*/
static const char id_chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"_0123456789";
#define ISIDCHAR(c) \
((c) && strchr(id_chars, (c)) != NULL)
/*
* Forward declarations of static functions.
*/
static void freedyncols(STMT *s);
static void freeresult(STMT *s, int clrcols);
static void unbindcols(STMT *s);
static SQLRETURN drvexecute(SQLHSTMT stmt, int initial);
static SQLRETURN freestmt(HSTMT stmt);
static SQLRETURN mkbindcols(STMT *s, int ncols);
static SQLRETURN setupparbuf(STMT *s, BINDPARM *p);
static SQLRETURN starttran(STMT *s);
static SQLRETURN substparam(STMT *s, int pnum, char **outp);
#if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE)
/* MS Access hack part 1 (reserved error -7748) */
static COL *statSpec2P, *statSpec3P;
#endif
#if (MEMORY_DEBUG < 1)
/**
* Duplicate string using xmalloc().
* @param str string to be duplicated
* @result pointer to new string or NULL
*/
static char *
strdup_(const char *str)
{
char *p = NULL;
if (str) {
p = xmalloc(strlen(str) + 1);
if (p) {
strcpy(p, str);
}
}
return p;
}
#endif
#ifdef WINTERFACE
/**
* Return length of UNICODE string.
* @param str UNICODE string
* @result length of string
*/
static int
uc_strlen(SQLWCHAR *str)
{
int len = 0;
if (str) {
while (*str) {
++len;
++str;
}
}
return len;
}
/**
* Copy UNICODE string like strncpy().
* @param dest destination area
* @param src source area
* @param len length of source area
* @return pointer to destination area
*/
static SQLWCHAR *
uc_strncpy(SQLWCHAR *dest, SQLWCHAR *src, int len)
{
int i = 0;
while (i < len) {
if (!src[i]) {
break;
}
dest[i] = src[i];
++i;
}
if (i < len) {
dest[i] = 0;
}
return dest;
}
/**
* Make UNICODE string from UTF8 string into buffer.
* @param str UTF8 string to be converted
* @param len length of str or -1
* @param uc destination area to receive UNICODE string
* @param ucLen byte length of destination area
*/
static void
uc_from_utf_buf(unsigned char *str, int len, SQLWCHAR *uc, int ucLen)
{
ucLen = ucLen / sizeof (SQLWCHAR);
if (!uc || ucLen < 0) {
return;
}
if (len < 0) {
len = ucLen * 5;
}
uc[0] = 0;
if (str) {
int i = 0;
while (i < len && *str && i < ucLen) {
unsigned char c = str[0];
if (c < 0x80) {
uc[i++] = c;
++str;
} else if (c <= 0xc1 || c >= 0xf5) {
/* illegal, ignored */
++str;
} else if (c < 0xe0) {
if ((str[1] & 0xc0) == 0x80) {
unsigned long t = ((c & 0x1f) << 6) | (str[1] & 0x3f);
uc[i++] = t;
str += 2;
} else {
uc[i++] = c;
++str;
}
} else if (c < 0xf0) {
if ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80) {
unsigned long t = ((c & 0x0f) << 12) |
((str[1] & 0x3f) << 6) | (str[2] & 0x3f);
uc[i++] = t;
str += 3;
} else {
uc[i++] = c;
++str;
}
} else if (c < 0xf8) {
if ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 &&
(str[3] & 0xc0) == 0x80) {
unsigned long t = ((c & 0x03) << 18) |
((str[1] & 0x3f) << 12) | ((str[2] & 0x3f) << 6) |
(str[4] & 0x3f);
if (sizeof (SQLWCHAR) == 2 * sizeof (char) &&
t >= 0x10000) {
t -= 0x10000;
uc[i++] = 0xd800 | (t & 0x3ff);
if (i >= ucLen) {
break;
}
t = 0xdc00 | ((t >> 10) & 0x3ff);
}
uc[i++] = t;
str += 4;
} else {
uc[i++] = c;
++str;
}
} else if (c < 0xfc) {
if ((str[1] & 0xc0) == 0x80 && (str[2] & 0xc0) == 0x80 &&
(str[3] & 0xc0) == 0x80 && (str[4] & 0xc0) == 0x80) {
unsigned long t = ((c & 0x01) << 24) |
((str[1] & 0x3f) << 18) | ((str[2] & 0x3f) << 12) |
((str[4] & 0x3f) << 6) | (str[5] & 0x3f);
if (sizeof (SQLWCHAR) == 2 * sizeof (char) &&
t >= 0x10000) {
t -= 0x10000;
uc[i++] = 0xd800 | (t & 0x3ff);
if (i >= ucLen) {
break;
}
t = 0xdc00 | ((t >> 10) & 0x3ff);
}
uc[i++] = t;
str += 5;
} else {
uc[i++] = c;
++str;
}
} else {
/* ignore */
++str;
}
}
if (i < ucLen) {
uc[i] = 0;
}
}
}
/**
* Make UNICODE string from UTF8 string.
* @param str UTF8 string to be converted
* @param len length of UTF8 string
* @return alloc'ed UNICODE string to be free'd by uc_free()
*/
static SQLWCHAR *
uc_from_utf(unsigned char *str, int len)
{
SQLWCHAR *uc = NULL;
int ucLen;
if (str) {
if (len == SQL_NTS) {
len = strlen((char *) str);
}
ucLen = sizeof (SQLWCHAR) * (len + 1);
uc = xmalloc(ucLen);
if (uc) {
uc_from_utf_buf(str, len, uc, ucLen);
}
}
return uc;
}
/**
* Make UTF8 string from UNICODE string.
* @param str UNICODE string to be converted
* @param len length of UNICODE string in bytes
* @return alloc'ed UTF8 string to be free'd by uc_free()
*/
static char *
uc_to_utf(SQLWCHAR *str, int len)
{
int i;
char *cp, *ret = NULL;
if (!str) {
return ret;
}
if (len == SQL_NTS) {
len = uc_strlen(str);
} else {
len = len / sizeof (SQLWCHAR);
}
cp = xmalloc(len * 6 + 1);
if (!cp) {
return ret;
}
ret = cp;
for (i = 0; i < len; i++) {
unsigned long c = str[i];
if (sizeof (SQLWCHAR) == 2 * sizeof (char)) {
c &= 0xffff;
}
if (c < 0x80) {
*cp++ = c;
} else if (c < 0x800) {
*cp++ = 0xc0 | ((c >> 6) & 0x1f);
*cp++ = 0x80 | (c & 0x3f);
} else if (c < 0x10000) {
if (sizeof (SQLWCHAR) == 2 * sizeof (char) &&
c >= 0xd800 && c <= 0xdbff && i + 1 < len) {
unsigned long c2 = str[i + 1] & 0xffff;
if (c2 >= 0xdc00 && c <= 0xdfff) {
c = ((c & 0x3ff) | ((c2 & 0x3ff) << 10)) + 0x10000;
*cp++ = 0xf0 | ((c >> 18) & 0x07);
*cp++ = 0x80 | ((c >> 12) & 0x3f);
*cp++ = 0x80 | ((c >> 6) & 0x3f);
*cp++ = 0x80 | (c & 0x3f);
++i;
continue;
}
}
*cp++ = 0xe0 | ((c >> 12) & 0x0f);
*cp++ = 0x80 | ((c >> 6) & 0x3f);
*cp++ = 0x80 | (c & 0x3f);
} else if (c < 0x200000) {
*cp++ = 0xf0 | ((c >> 18) & 0x07);
*cp++ = 0x80 | ((c >> 12) & 0x3f);
*cp++ = 0x80 | ((c >> 6) & 0x3f);
*cp++ = 0x80 | (c & 0x3f);
} else if (c < 0x4000000) {
*cp++ = 0xf8 | ((c >> 24) & 0x03);
*cp++ = 0x80 | ((c >> 18) & 0x3f);
*cp++ = 0x80 | ((c >> 12) & 0x3f);
*cp++ = 0x80 | ((c >> 6) & 0x3f);
*cp++ = 0x80 | (c & 0x3f);
} else if (c < 0x80000000) {
*cp++ = 0xfc | ((c >> 31) & 0x01);
*cp++ = 0x80 | ((c >> 24) & 0x3f);
*cp++ = 0x80 | ((c >> 18) & 0x3f);
*cp++ = 0x80 | ((c >> 12) & 0x3f);
*cp++ = 0x80 | ((c >> 6) & 0x3f);
*cp++ = 0x80 | (c & 0x3f);
}
}
*cp = '\0';
return ret;
}
/**
* Make UTF8 string from UNICODE string.
* @param str UNICODE string to be converted
* @param len length of UNICODE string in characters
* @return alloc'ed UTF8 string to be free'd by uc_free()
*/
static char *
uc_to_utf_c(SQLWCHAR *str, int len)
{
if (len != SQL_NTS) {
len = len * sizeof (SQLWCHAR);
}
return uc_to_utf(str, len);
}
/**
* Free converted UTF8 or UNICODE string.
*/
static void
uc_free(void *str)
{
if (str) {
xfree(str);
}
}
#endif
#ifdef USE_DLOPEN_FOR_GPPS
#include <dlfcn.h>
#define SQLGetPrivateProfileString(A,B,C,D,E,F) drvgpps(d,A,B,C,D,E,F)
/*
* EXPERIMENTAL: SQLGetPrivateProfileString infrastructure using
* dlopen(), in theory this makes the driver independent from the
* driver manager, i.e. the same driver binary can run with iODBC
* and unixODBC.
*/
static void
drvgetgpps(DBC *d)
{
void *lib;
int (*gpps)();
lib = dlopen("libodbcinst.so.1", RTLD_LAZY);
if (!lib) {
lib = dlopen("libodbcinst.so", RTLD_LAZY);
}
if (!lib) {
lib = dlopen("libiodbcinst.so.2", RTLD_LAZY);
}
if (!lib) {
lib = dlopen("libiodbcinst.so", RTLD_LAZY);
}
if (lib) {
gpps = (int (*)()) dlsym(lib, "SQLGetPrivateProfileString");
if (!gpps) {
dlclose(lib);
return;
}
d->instlib = lib;
d->gpps = gpps;
}
}
static void
drvrelgpps(DBC *d)
{
if (d->instlib) {
dlclose(d->instlib);
d->instlib = 0;
}
}
static int
drvgpps(DBC *d, char *sect, char *ent, char *def, char *buf,
int bufsiz, char *fname)
{
if (d->gpps) {
return d->gpps(sect, ent, def, buf, bufsiz, fname);
}
strncpy(buf, def, bufsiz);
buf[bufsiz - 1] = '\0';
return 1;
}
#else
#include <odbcinst.h>
#define drvgetgpps(d)
#define drvrelgpps(d)
#endif
/**
* Set error message and SQL state on DBC
* @param d database connection pointer
* @param naterr native error code
* @param msg error message
* @param st SQL state
*/
#if defined(__GNUC__) && (__GNUC__ >= 2)
static void setstatd(DBC *, int, char *, char *, ...)
__attribute__((format (printf, 3, 5)));
#endif
static void
setstatd(DBC *d, int naterr, char *msg, char *st, ...)
{
va_list ap;
if (!d) {
return;
}
d->naterr = naterr;
d->logmsg[0] = '\0';
if (msg) {
int count;
va_start(ap, st);
count = vsnprintf((char *) d->logmsg, sizeof (d->logmsg), msg, ap);
va_end(ap);
if (count < 0) {
d->logmsg[sizeof (d->logmsg) - 1] = '\0';
}
}
if (!st) {
st = "?????";
}
strncpy(d->sqlstate, st, 5);
d->sqlstate[5] = '\0';
}
/**
* Set error message and SQL state on statement
* @param s statement pointer
* @param naterr native error code
* @param msg error message
* @param st SQL state
*/
#if defined(__GNUC__) && (__GNUC__ >= 2)
static void setstat(STMT *, int, char *, char *, ...)
__attribute__((format (printf, 3, 5)));
#endif
static void
setstat(STMT *s, int naterr, char *msg, char *st, ...)
{
va_list ap;
if (!s) {
return;
}
s->naterr = naterr;
s->logmsg[0] = '\0';
if (msg) {
int count;
va_start(ap, st);
count = vsnprintf((char *) s->logmsg, sizeof (s->logmsg), msg, ap);
va_end(ap);
if (count < 0) {
s->logmsg[sizeof (s->logmsg) - 1] = '\0';
}
}
if (!st) {
st = "?????";
}
strncpy(s->sqlstate, st, 5);
s->sqlstate[5] = '\0';
}
/**
* Report IM001 (not implemented) SQL error code for HDBC.
* @param dbc database connection handle
* @result ODBC error code
*/
static SQLRETURN
drvunimpldbc(HDBC dbc)
{
DBC *d;
if (dbc == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
d = (DBC *) dbc;
setstatd(d, -1, "not supported", "IM001");
return SQL_ERROR;
}
/**
* Report IM001 (not implemented) SQL error code for HSTMT.
* @param stmt statement handle
* @result ODBC error code
*/
static SQLRETURN
drvunimplstmt(HSTMT stmt)
{
STMT *s;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
setstat(s, -1, "not supported", "IM001");
return SQL_ERROR;
}
/**
* Free memory given pointer to memory pointer.
* @param x pointer to pointer to memory to be free'd
*/
static void
freep(void *x)
{
if (x && ((char **) x)[0]) {
xfree(((char **) x)[0]);
((char **) x)[0] = NULL;
}
}
/**
* Report S1000 (out of memory) SQL error given STMT.
* @param s statement pointer
* @result ODBC error code
*/
static SQLRETURN
nomem(STMT *s)
{
setstat(s, -1, "out of memory", (*s->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
/**
* Report S1000 (not connected) SQL error given STMT.
* @param s statement pointer
* @result ODBC error code
*/
static SQLRETURN
noconn(STMT *s)
{
setstat(s, -1, "not connected", (*s->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
/**
* Internal locale neutral strtod function.
* @param data pointer to string
* @param endp pointer for ending character
* @result double value
*/
#if defined(HAVE_SQLITEATOF) && HAVE_SQLITEATOF
extern double sqliteAtoF(char *data, char **endp);
#define ln_strtod sqliteAtoF
#else
static double
ln_strtod(const char *data, char **endp)
{
#if defined(HAVE_LOCALECONV) || defined(_WIN32) || defined(_WIN64)
struct lconv *lc;
char buf[128], *p, *end;
double value;
lc = localeconv();
if (lc && lc->decimal_point && lc->decimal_point[0] &&
lc->decimal_point[0] != '.') {
strncpy(buf, data, sizeof (buf) - 1);
buf[sizeof (buf) - 1] = '\0';
p = strchr(buf, '.');
if (p) {
*p = lc->decimal_point[0];
}
p = buf;
} else {
p = (char *) data;
}
value = strtod(p, &end);
end = (char *) data + (end - p);
if (endp) {
*endp = end;
}
return value;
#else
return strtod(data, endp);
#endif
}
#endif
#if !defined(HAVE_SQLITEMPRINTF) || !HAVE_SQLITEMPRINTF
/**
* Internal locale neutral sprintf("%g") function.
* @param buf pointer to string
* @param value double value
*/
static void
ln_sprintfg(char *buf, double value)
{
#if defined(HAVE_LOCALECONV) || defined(_WIN32) || defined(_WIN64)
struct lconv *lc;
char *p;
sprintf(buf, "%.16g", value);
lc = localeconv();
if (lc && lc->decimal_point && lc->decimal_point[0] &&
lc->decimal_point[0] != '.') {
p = strchr(buf, lc->decimal_point[0]);
if (p) {
*p = '.';
}
}
#else
sprintf(buf, "%.16g", value);
#endif
}
#endif
/**
* Strip quotes from quoted string in-place.
* @param str string
*/
static char *
unquote(char *str)
{
if (str) {
int len = strlen(str);
if (len > 1) {
if ((str[0] == '\'' && str[len - 1] == '\'') ||
(str[0] == '"' && str[len - 1] == '"') ||
(str[0] == '[' && str[len - 1] == ']')) {
str[len - 1] = '\0';
strcpy(str, str + 1);
}
}
}
return str;
}
/**
* Unescape search pattern for e.g. table name in
* catalog functions. Replacements in string are done in-place.
* @param str string
* @result number of pattern characters in string or 0
*/
static int
unescpat(char *str)
{
char *p, *q;
int count = 0;
p = str;
while ((q = strchr(p, '_')) != NULL) {
if (q == str || q[-1] != '\\') {
count++;
}
p = q + 1;
}
p = str;
while ((q = strchr(p, '%')) != NULL) {
if (q == str || q[-1] != '\\') {
count++;
}
p = q + 1;
}
p = str;
while ((q = strchr(p, '\\')) != NULL) {
if (q[1] == '\\' || q[1] == '_' || q[1] == '%') {
strcpy(q, q + 1);
}
p = q + 1;
}
return count;
}
/**
* SQL LIKE string match with optional backslash escape handling.
* @param str string
* @param pat pattern
* @param esc when true, treat literally "\\" as "\", "\?" as "?", "\_" as "_"
* @result true when pattern matched
*/
static int
namematch(char *str, char *pat, int esc)
{
int cp, ch;
while (1) {
cp = TOLOWER(*pat);
if (cp == '\0') {
if (*str != '\0') {
goto nomatch;
}
break;
}
if (*str == '\0' && cp != '%') {
goto nomatch;
}
if (cp == '%') {
while (*pat == '%') {
++pat;
}
cp = TOLOWER(*pat);
if (cp == '\0') {
break;
}
while (1) {
if (cp != '_' && cp != '\\') {
while (*str) {
ch = TOLOWER(*str);
if (ch == cp) {
break;
}
++str;
}
}
if (namematch(str, pat, esc)) {
goto match;
}
if (*str == '\0') {
goto nomatch;
}
ch = TOLOWER(*str);
++str;
}
}
if (cp == '_') {
pat++;
str++;
continue;
}
if (esc && cp == '\\' &&
(pat[1] == '\\' || pat[1] == '%' || pat[1] == '_')) {
++pat;
cp = TOLOWER(*pat);
}
ch = TOLOWER(*str++);
++pat;
if (ch != cp) {
goto nomatch;
}
}
match:
return 1;
nomatch:
return 0;
}
/**
* Busy callback for SQLite.
* @param udata user data, pointer to DBC
* @param table table name or NULL
* @param count count of subsequenct calls
* @result true of false
*/
static int
busy_handler(void *udata, const char *table, int count)
{
DBC *d = (DBC *) udata;
long t1;
int ret = 0;
#if !defined(_WIN32) && !defined(_WIN64)
struct timeval tv;
#endif
if (d->busyint) {
d->busyint = 0;
return ret;
}
if (d->timeout <= 0) {
return ret;
}
if (count <= 1) {
#if defined(_WIN32) || defined(_WIN64)
d->t0 = GetTickCount();
#else
gettimeofday(&tv, NULL);
d->t0 = tv.tv_sec * 1000 + tv.tv_usec / 1000;
#endif
}
#if defined(_WIN32) || defined(_WIN64)
t1 = GetTickCount();
#else
gettimeofday(&tv, NULL);
t1 = tv.tv_sec * 1000 + tv.tv_usec / 1000;
#endif
if (t1 - d->t0 > d->timeout) {
goto done;
}
#if defined(_WIN32) || defined(_WIN64)
Sleep(10);
#else
#ifdef HAVE_USLEEP
usleep(10000);
#else
tv.tv_sec = 0;
tv.tv_usec = 10000;
select(0, NULL, NULL, NULL, &tv);
#endif
#endif
ret = 1;
done:
return ret;
}
#ifdef HAVE_ENCDEC
static void hextobin_func(sqlite_func *context, int argc, const char **argv);
static void bintohex_func(sqlite_func *context, int argc, const char **argv);
#endif
static void time_func(sqlite_func *context, int argc, const char **argv);
/**
* Set SQLite options (PRAGMAs) given SQLite handle.
* @param x SQLite database handle
* @param d DBC pointer
* @result SQLite error code
*
* "full_column_names" is always turned on to get the table
* names in column labels. "count_changes" is always turned
* on to get the number of affected rows for INSERTs et.al.
* "empty_result_callbacks" is always turned on to get
* column labels even when no rows are returned.
* Starting from SQLite 2.6.0 data types are reported for
* callback functions when the "show_datatypes" pragma is
* asserted.
*/
static int
setsqliteopts(sqlite *x, DBC *d)
{
int count = 0, step = 0, rc;
while (step < 4) {
if (step < 1) {
rc = sqlite_exec(x, "PRAGMA full_column_names = on;",
NULL, NULL, NULL);
} else if (step < 2) {
rc = sqlite_exec(x, "PRAGMA count_changes = on;",
NULL, NULL, NULL);
} else if (step < 3) {
rc = sqlite_exec(x, "PRAGMA empty_result_callbacks = on;",
NULL, NULL, NULL);
} else {
rc = sqlite_exec(x, "PRAGMA show_datatypes = on;",
NULL, NULL, NULL);
}
if (rc != SQLITE_OK) {
if (rc != SQLITE_BUSY ||
!busy_handler((void *) d, NULL, ++count)) {
return rc;
}
continue;
}
count = 0;
++step;
}
sqlite_busy_handler(x, busy_handler, (void *) d);
#if HAVE_ENCDEC
{
char *fname;
fname = "hextobin";
sqlite_create_function(x, fname, 1, hextobin_func, 0);
sqlite_function_type(x, fname, SQLITE_TEXT);
fname = "bintohex";
sqlite_create_function(x, fname, 1, bintohex_func, 0);
sqlite_function_type(x, fname, SQLITE_TEXT);
}
#endif
{
char *fname;
fname = "current_time_local";
sqlite_create_function(x, fname, 0, time_func, (void *) 0);
sqlite_function_type(x, fname, SQLITE_TEXT);
fname = "current_time_utc";
sqlite_create_function(x, fname, 0, time_func, (void *) 1);
sqlite_function_type(x, fname, SQLITE_TEXT);
fname = "current_date_local";
sqlite_create_function(x, fname, 0, time_func, (void *) 2);
sqlite_function_type(x, fname, SQLITE_TEXT);
fname = "current_date_utc";
sqlite_create_function(x, fname, 0, time_func, (void *) 3);
sqlite_function_type(x, fname, SQLITE_TEXT);
fname = "current_datetime_local";
sqlite_create_function(x, fname, 0, time_func, (void *) 4);
sqlite_function_type(x, fname, SQLITE_TEXT);
fname = "current_datetime_utc";
sqlite_create_function(x, fname, 0, time_func, (void *) 5);
sqlite_function_type(x, fname, SQLITE_TEXT);
fname = "current_timestamp_local";
sqlite_create_function(x, fname, 0, time_func, (void *) 4);
sqlite_function_type(x, fname, SQLITE_TEXT);
fname = "current_timestamp_utc";
sqlite_create_function(x, fname, 0, time_func, (void *) 5);
sqlite_function_type(x, fname, SQLITE_TEXT);
}
return SQLITE_OK;
}
/**
* Free counted array of char pointers.
* @param rowp pointer to char pointer array
*
* The -1-th element of the array holds the array size.
* All non-NULL pointers of the array and then the array
* itself are free'd.
*/
static void
freerows(char **rowp)
{
int size, i;
if (!rowp) {
return;
}
--rowp;
size = (PTRDIFF_T) rowp[0];
for (i = 1; i <= size; i++) {
freep(&rowp[i]);
}
freep(&rowp);
}
/**
* Map SQL field type from string to ODBC integer type code.
* @param typename field type string
* @param nosign pointer to indicator for unsigned field or NULL
* @param ov3 boolean, true for SQL_OV_ODBC3
* @param nowchar boolean, for WINTERFACE don't use WCHAR
* @result SQL data type
*/
static int
mapsqltype(const char *typename, int *nosign, int ov3, int nowchar)
{
char *p, *q;
int testsign = 0, result;
#ifdef WINTERFACE
result = nowchar ? SQL_VARCHAR : SQL_WVARCHAR;
#else
result = SQL_VARCHAR;
#endif
if (!typename) {
return result;
}
q = p = xmalloc(strlen(typename) + 1);
if (!p) {
return result;
}
strcpy(p, typename);
while (*q) {
*q = TOLOWER(*q);
++q;
}
if (strncmp(p, "inter", 5) == 0) {
} else if (strncmp(p, "int", 3) == 0 ||
strncmp(p, "mediumint", 9) == 0) {
testsign = 1;
result = SQL_INTEGER;
} else if (strncmp(p, "numeric", 7) == 0) {
result = SQL_DOUBLE;
} else if (strncmp(p, "tinyint", 7) == 0) {
testsign = 1;
result = SQL_TINYINT;
} else if (strncmp(p, "smallint", 8) == 0) {
testsign = 1;
result = SQL_SMALLINT;
} else if (strncmp(p, "float", 5) == 0) {
result = SQL_DOUBLE;
} else if (strncmp(p, "double", 6) == 0 ||
strncmp(p, "real", 4) == 0) {
result = SQL_DOUBLE;
} else if (strncmp(p, "timestamp", 9) == 0) {
#ifdef SQL_TYPE_TIMESTAMP
result = ov3 ? SQL_TYPE_TIMESTAMP : SQL_TIMESTAMP;
#else
result = SQL_TIMESTAMP;
#endif
} else if (strncmp(p, "datetime", 8) == 0) {
#ifdef SQL_TYPE_TIMESTAMP
result = ov3 ? SQL_TYPE_TIMESTAMP : SQL_TIMESTAMP;
#else
result = SQL_TIMESTAMP;
#endif
} else if (strncmp(p, "time", 4) == 0) {
#ifdef SQL_TYPE_TIME
result = ov3 ? SQL_TYPE_TIME : SQL_TIME;
#else
result = SQL_TIME;
#endif
} else if (strncmp(p, "date", 4) == 0) {
#ifdef SQL_TYPE_DATE
result = ov3 ? SQL_TYPE_DATE : SQL_DATE;
#else
result = SQL_DATE;
#endif
#ifdef SQL_LONGVARCHAR
} else if (strncmp(p, "text", 4) == 0 ||
strncmp(p, "memo", 4) == 0) {
#ifdef WINTERFACE
result = nowchar ? SQL_LONGVARCHAR : SQL_WLONGVARCHAR;
#else
result = SQL_LONGVARCHAR;
#endif
#ifdef WINTERFACE
} else if (strncmp(p, "wtext", 5) == 0 ||
strncmp(p, "wvarchar", 8) == 0 ||
strncmp(p, "longwvarchar", 12) == 0) {
result = SQL_WLONGVARCHAR;
#endif
#endif
#if HAVE_ENCDEC
} else if (strncmp(p, "binary", 6) == 0 ||
strncmp(p, "varbinary", 9) == 0 ||
strncmp(p, "bytea", 5) == 0 ||
strncmp(p, "blob", 4) == 0 ||
strncmp(p, "tinyblob", 8) == 0 ||
strncmp(p, "mediumblob", 10) == 0) {
result = SQL_VARBINARY;
} else if (strncmp(p, "longbinary", 10) == 0 ||
strncmp(p, "longvarbinary", 13) == 0 ||
strncmp(p, "longblob", 8) == 0) {
result = SQL_LONGVARBINARY;
#endif
#ifdef SQL_BIT
} else if (strncmp(p, "bool", 4) == 0 ||
strncmp(p, "bit", 3) == 0) {
testsign = 0;
result = SQL_BIT;
#endif
}
if (nosign) {
if (testsign) {
*nosign = strstr(p, "unsigned") != NULL;
} else {
*nosign = 1;
}
}
xfree(p);
return result;
}
/**
* Get maximum display size and number of digits after decimal point
* from field type specification.
* @param typename field type specification
* @param sqltype target SQL data type
* @param mp pointer to maximum display size or NULL
* @param dp pointer to number of digits after decimal point or NULL
*/
static void
getmd(const char *typename, int sqltype, int *mp, int *dp)
{
int m = 0, d = 0;
switch (sqltype) {
case SQL_INTEGER: m = 10; d = 9; break;
case SQL_TINYINT: m = 4; d = 3; break;
case SQL_SMALLINT: m = 6; d = 5; break;
case SQL_FLOAT: m = 25; d = 24; break;
case SQL_DOUBLE: m = 54; d = 53; break;
case SQL_VARCHAR: m = 255; d = 0; break;
#ifdef WINTERFACE
#ifdef SQL_WVARCHAR
case SQL_WVARCHAR: m = 255; d = 0; break;
#endif
#endif
#ifdef SQL_TYPE_DATE
case SQL_TYPE_DATE:
#endif
case SQL_DATE: m = 10; d = 0; break;
#ifdef SQL_TYPE_TIME
case SQL_TYPE_TIME:
#endif
case SQL_TIME: m = 8; d = 0; break;
#ifdef SQL_TYPE_TIMESTAMP
case SQL_TYPE_TIMESTAMP:
#endif
case SQL_TIMESTAMP: m = 32; d = 0; break;
#ifdef SQL_LONGVARCHAR
case SQL_LONGVARCHAR : m = 65536; d = 0; break;
#endif
#ifdef WINTERFACE
#ifdef SQL_WLONGVARCHAR
case SQL_WLONGVARCHAR: m = 65536; d = 0; break;
#endif
#endif
#if HAVE_ENCDEC
case SQL_VARBINARY: m = 255; d = 0; break;
case SQL_LONGVARBINARY: m = 65536; d = 0; break;
#endif
#ifdef SQL_BIT
case SQL_BIT: m = 1; d = 1; break;
#endif
}
if (m && typename) {
int mm, dd;
if (sscanf(typename, "%*[^(](%d)", &mm) == 1) {
m = d = mm;
} else if (sscanf(typename, "%*[^(](%d,%d)", &mm, &dd) == 2) {
m = mm;
d = dd;
}
}
if (mp) {
*mp = m;
}
if (dp) {
*dp = d;
}
}
/**
* Map SQL_C_DEFAULT to proper C type.
* @param type input C type
* @param stype input SQL type
* @param nosign 0=signed, 0>unsigned, 0<undefined
* @param nowchar when compiled with WINTERFACE don't use WCHAR
* @result C type
*/
static int
mapdeftype(int type, int stype, int nosign, int nowchar)
{
if (type == SQL_C_DEFAULT) {
switch (stype) {
case SQL_INTEGER:
type = (nosign > 0) ? SQL_C_ULONG : SQL_C_LONG;
break;
case SQL_TINYINT:
type = (nosign > 0) ? SQL_C_UTINYINT : SQL_C_TINYINT;
break;
case SQL_SMALLINT:
type = (nosign > 0) ? SQL_C_USHORT : SQL_C_SHORT;
break;
case SQL_FLOAT:
type = SQL_C_FLOAT;
break;
case SQL_DOUBLE:
type = SQL_C_DOUBLE;
break;
case SQL_TIMESTAMP:
type = SQL_C_TIMESTAMP;
break;
case SQL_TIME:
type = SQL_C_TIME;
break;
case SQL_DATE:
type = SQL_C_DATE;
break;
#ifdef SQL_C_TYPE_TIMESTAMP
case SQL_TYPE_TIMESTAMP:
type = SQL_C_TYPE_TIMESTAMP;
break;
#endif
#ifdef SQL_C_TYPE_TIME
case SQL_TYPE_TIME:
type = SQL_C_TYPE_TIME;
break;
#endif
#ifdef SQL_C_TYPE_DATE
case SQL_TYPE_DATE:
type = SQL_C_TYPE_DATE;
break;
#endif
#ifdef WINTERFACE
case SQL_WVARCHAR:
case SQL_WCHAR:
#ifdef SQL_WLONGVARCHAR
case SQL_WLONGVARCHAR:
#endif
type = nowchar ? SQL_C_CHAR : SQL_C_WCHAR;
break;
#endif
#if HAVE_ENCDEC
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
type = SQL_C_BINARY;
break;
#endif
#ifdef SQL_BIT
case SQL_BIT:
type = SQL_C_BIT;
break;
#endif
default:
#ifdef WINTERFACE
type = nowchar ? SQL_C_CHAR : SQL_C_WCHAR;
#else
type = SQL_C_CHAR;
#endif
break;
}
}
return type;
}
/**
* Fixup query string with optional parameter markers.
* @param sql original query string
* @param sqlLen length of query string or SQL_NTS
* @param nparam output number of parameters
* @param isselect output indicator for SELECT statement
* @param errmsg output error message
* @param version SQLite version information
* @param namepp pointer to parameter names array
* @result newly allocated string containing query string for SQLite or NULL
*/
static char *
fixupsql(char *sql, int sqlLen, int *nparam, int *isselect, char **errmsg,
int version, char ***namepp)
{
char *q = sql, *qz = NULL, *p, *inq = NULL, *out;
int np = 0, isddl = -1, size;
char **npp = NULL, *ncp = NULL;
*errmsg = NULL;
if (sqlLen != SQL_NTS) {
qz = q = xmalloc(sqlLen + 1);
if (!qz) {
return NULL;
}
memcpy(q, sql, sqlLen);
q[sqlLen] = '\0';
size = sqlLen * 4;
} else {
size = strlen(sql) * 4;
}
size += sizeof (char *) - 1;
size &= ~(sizeof (char *) - 1);
p = xmalloc(2 * size + size * sizeof (char *) / 2);
if (!p) {
errout:
freep(&qz);
return NULL;
}
memset(p, 0, 2 * size + size * sizeof (char *) / 2);
out = p;
npp = (char **) (out + size);
ncp = (char *) npp + size * sizeof (char *) / 2;
while (*q) {
switch (*q) {
case '\'':
case '\"':
if (q == inq) {
inq = NULL;
} else if (!inq) {
inq = q + 1;
while (*inq) {
if (*inq == *q) {
if (inq[1] == *q) {
inq++;
} else {
break;
}
}
inq++;
}
}
*p++ = *q;
break;
case '?':
if (inq) {
*p++ = *q;
} else {
*p++ = '%';
*p++ = 'Q';
npp[np] = ncp;
*ncp = '\0';
++ncp;
np++;
}
break;
case ':': /* ORACLE-style named parameter */
case '@': /* ADO.NET-style named parameter */
if (inq) {
*p++ = *q;
} else {
int n = -1;
do {
++q;
++n;
} while (*q && ISIDCHAR(*q));
if (n > 0) {
*p++ = '%';
*p++ = 'Q';
npp[np] = ncp;
memcpy(ncp, q - n, n);
ncp[n] = '\0';
ncp += n + 1;
np++;
}
--q;
}
break;
case ';':
if (!inq) {
if (isddl < 0) {
char *qq = out;
while (*qq && ISSPACE(*qq)) {
++qq;
}
if (*qq && *qq != ';') {
size = strlen(qq);
if ((size >= 5) &&
(strncasecmp(qq, "create", 5) == 0)) {
isddl = 1;
} else if ((size >= 4) &&
(strncasecmp(qq, "drop", 4) == 0)) {
isddl = 1;
} else {
isddl = 0;
}
}
}
if (isddl == 0) {
char *qq = q;
do {
++qq;
} while (*qq && ISSPACE(*qq));
if (*qq && *qq != ';') {
freep(&out);
*errmsg = "only one SQL statement allowed";
goto errout;
}
}
}
*p++ = *q;
break;
case '%':
*p++ = '%';
*p++ = '%';
break;
case '{':
/* deal with {d 'YYYY-MM-DD'}, {t ...}, and {ts ...} */
if (!inq) {
char *end = q + 1;
while (*end && *end != '}') {
++end;
}
if (*end == '}') {
char *start = q + 1;
char *end2 = end - 1;
while (start < end2 && *start != '\'') {
++start;
}
while (end2 > start && *end2 != '\'') {
--end2;
}
if (*start == '\'' && *end2 == '\'') {
while (start <= end2) {
*p++ = *start;
++start;
}
q = end;
break;
}
}
}
/* FALL THROUGH */
default:
*p++ = *q;
}
++q;
}
freep(&qz);
*p = '\0';
if (nparam) {
*nparam = np;
}
if (isselect) {
if (isddl > 0) {
*isselect = 0;
} else {
p = out;
while (*p && ISSPACE(*p)) {
++p;
}
size = strlen(p);
*isselect = (size >= 6) && (strncasecmp(p, "select", 6) == 0);
}
}
if (namepp) {
*namepp = npp;
}
return out;
}
/**
* Find column given name in string array.
* @param cols string array
* @param ncols number of strings
* @param name column name
* @result >= 0 on success, -1 on error
*/
static int
findcol(char **cols, int ncols, char *name)
{
int i;
if (cols) {
for (i = 0; i < ncols; i++) {
if (strcmp(cols[i], name) == 0) {
return i;
}
}
}
return -1;
}
/**
* Fixup column information for a running statement.
* @param s statement to get fresh column information
* @param sqlite SQLite database handle
* @param types column types or NULL
*
* The column labels get the table names stripped
* when there's more than one column and all table
* names are identical.
*
* The "dyncols" field of STMT is filled with column
* information obtained by SQLite "PRAGMA table_info"
* for each column whose table name is known. If the
* types are already present as with SQLite 2.5.7
* this information is used instead.
*/
static void
fixupdyncols(STMT *s, sqlite *sqlite, const char **types)
{
int i, k, pk, nn, t, r, nrows, ncols;
char **rowp, *flagp, flags[128];
if (!s->dyncols) {
return;
}
/* fixup labels */
if (!s->longnames) {
if (s->dcols > 1) {
char *table = s->dyncols[0].table;
for (i = 1; table[0] && i < s->dcols; i++) {
if (strcmp(s->dyncols[i].table, table)) {
break;
}
}
if (i >= s->dcols) {
for (i = 0; i < s->dcols; i++) {
s->dyncols[i].label = s->dyncols[i].column;
}
}
} else if (s->dcols == 1) {
s->dyncols[0].label = s->dyncols[0].column;
}
}
if (types) {
for (i = 0; i < s->dcols; i++) {
freep(&s->dyncols[i].typename);
s->dyncols[i].typename = xstrdup(types[i] ? types[i] : "text");
s->dyncols[i].type =
mapsqltype(types[i], &s->dyncols[i].nosign, *s->ov3,
s->nowchar[0] || s->nowchar[1]);
getmd(types[i], s->dyncols[i].type, &s->dyncols[i].size, NULL);
#ifdef SQL_LONGVARCHAR
if (s->dyncols[i].type == SQL_VARCHAR &&
s->dyncols[i].size > 255) {
s->dyncols[i].type = SQL_LONGVARCHAR;
}
#endif
#ifdef WINTERFACE
#ifdef SQL_WLONGVARCHAR
if (s->dyncols[i].type == SQL_WVARCHAR &&
s->dyncols[i].size > 255) {
s->dyncols[i].type = SQL_WLONGVARCHAR;
}
#endif
#endif
#if HAVE_ENCDEC
if (s->dyncols[i].type == SQL_VARBINARY &&
s->dyncols[i].size > 255) {
s->dyncols[i].type = SQL_LONGVARBINARY;
}
#endif
}
}
if (s->dcols > array_size(flags)) {
flagp = xmalloc(sizeof (flags[0]) * s->dcols);
if (flagp == NULL) {
return;
}
} else {
flagp = flags;
}
memset(flagp, 0, sizeof (flags[0]) * s->dcols);
for (i = 0; i < s->dcols; i++) {
s->dyncols[i].autoinc = SQL_FALSE;
s->dyncols[i].notnull = SQL_NULLABLE;
}
for (i = 0; i < s->dcols; i++) {
int ret, lastpk = -1, autoinccount = 0;
if (!s->dyncols[i].table[0]) {
continue;
}
if (flagp[i]) {
continue;
}
ret = sqlite_get_table_printf(sqlite,
"PRAGMA table_info('%q')", &rowp,
&nrows, &ncols, NULL,
s->dyncols[i].table);
if (ret != SQLITE_OK) {
continue;
}
k = findcol(rowp, ncols, "name");
t = findcol(rowp, ncols, "type");
pk = findcol(rowp, ncols, "pk");
nn = findcol(rowp, ncols, "notnull");
if (k < 0 || t < 0) {
goto freet;
}
for (r = 1; r <= nrows; r++) {
int m;
for (m = i; m < s->dcols; m++) {
if (!flagp[m] &&
strcmp(s->dyncols[m].column, rowp[r * ncols + k]) == 0 &&
strcmp(s->dyncols[m].table, s->dyncols[i].table) == 0) {
char *typename = rowp[r * ncols + t];
flagp[m] = 1;
freep(&s->dyncols[m].typename);
s->dyncols[m].typename = xstrdup(typename);
s->dyncols[m].type =
mapsqltype(typename, &s->dyncols[m].nosign, *s->ov3,
s->nowchar[0] || s->nowchar[1]);
getmd(typename, s->dyncols[m].type, &s->dyncols[m].size,
NULL);
#ifdef SQL_LONGVARCHAR
if (s->dyncols[m].type == SQL_VARCHAR &&
s->dyncols[m].size > 255) {
s->dyncols[m].type = SQL_LONGVARCHAR;
}
#endif
#ifdef WINTERFACE
#ifdef SQL_WLONGVARCHAR
if (s->dyncols[i].type == SQL_WVARCHAR &&
s->dyncols[i].size > 255) {
s->dyncols[i].type = SQL_WLONGVARCHAR;
}
#endif
#endif
#if HAVE_ENCDEC
if (s->dyncols[i].type == SQL_VARBINARY &&
s->dyncols[i].size > 255) {
s->dyncols[i].type = SQL_LONGVARBINARY;
}
#endif
if (pk >= 0 && strcmp(rowp[r * ncols + pk], "1") == 0) {
if (++autoinccount > 1) {
if (lastpk >= 0) {
s->dyncols[lastpk].autoinc = SQL_FALSE;
lastpk = -1;
}
} else {
lastpk = m;
if (strlen(typename) == 7 &&
strncasecmp(typename, "integer", 7) == 0) {
s->dyncols[m].autoinc = SQL_TRUE;
}
}
}
if (nn >= 0 && rowp[r * ncols + nn][0] != '0') {
s->dyncols[m].notnull = SQL_NO_NULLS;
}
}
}
}
freet:
sqlite_free_table(rowp);
}
if (flagp != flags) {
freep(&flagp);
}
}
/**
* Return number of month days.
* @param year
* @param month 1..12
* @result number of month days or 0
*/
static int
getmdays(int year, int month)
{
static const int mdays[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
int mday;
if (month < 1) {
return 0;
}
mday = mdays[(month - 1) % 12];
if (mday == 28 && year % 4 == 0 &&
(!(year % 100 == 0) || year % 400 == 0)) {
mday++;
}
return mday;
}
/**
* Convert string to ODBC DATE_STRUCT.
* @param str string to be converted
* @param ds output DATE_STRUCT
* @result 0 on success, -1 on error
*
* Strings of the format 'YYYYMMDD' or 'YYYY-MM-DD' or
* 'YYYY/MM/DD' are converted to a DATE_STRUCT.
*/
static int
str2date(char *str, DATE_STRUCT *ds)
{
int i, err = 0;
char *p, *q;
ds->year = ds->month = ds->day = 0;
p = str;
while (*p && !ISDIGIT(*p)) {
++p;
}
q = p;
i = 0;
while (*q && !ISDIGIT(*q)) {
++i;
++q;
}
if (i >= 8) {
char buf[8];
strncpy(buf, p + 0, 4); buf[4] = '\0';
ds->year = strtol(buf, NULL, 10);
strncpy(buf, p + 4, 2); buf[2] = '\0';
ds->month = strtol(buf, NULL, 10);
strncpy(buf, p + 6, 2); buf[2] = '\0';
ds->day = strtol(buf, NULL, 10);
goto done;
}
i = 0;
while (i < 3) {
int n;
q = NULL;
n = strtol(p, &q, 10);
if (!q || q == p) {
if (*q == '\0') {
if (i == 0) {
err = 1;
}
goto done;
}
}
if (*q == '-' || *q == '/' || *q == '\0' || i == 2) {
switch (i) {
case 0: ds->year = n; break;
case 1: ds->month = n; break;
case 2: ds->day = n; break;
}
++i;
if (*q) {
++q;
}
} else {
i = 0;
while (*q && !ISDIGIT(*q)) {
++q;
}
}
p = q;
}
done:
/* final check for overflow */
if (err ||
ds->month < 1 || ds->month > 12 ||
ds->day < 1 || ds->day > getmdays(ds->year, ds->month)) {
return -1;
}
return 0;
}
/**
* Convert string to ODBC TIME_STRUCT.
* @param str string to be converted
* @param ts output TIME_STRUCT
* @result 0 on success, -1 on error
*
* Strings of the format 'HHMMSS' or 'HH:MM:SS'
* are converted to a TIME_STRUCT.
*/
static int
str2time(char *str, TIME_STRUCT *ts)
{
int i, err = 0;
char *p, *q;
ts->hour = ts->minute = ts->second = 0;
p = str;
while (*p && !ISDIGIT(*p)) {
++p;
}
q = p;
i = 0;
while (*q && ISDIGIT(*q)) {
++i;
++q;
}
if (i >= 6) {
char buf[4];
strncpy(buf, p + 0, 2); buf[2] = '\0';
ts->hour = strtol(buf, NULL, 10);
strncpy(buf, p + 2, 2); buf[2] = '\0';
ts->minute = strtol(buf, NULL, 10);
strncpy(buf, p + 4, 2); buf[2] = '\0';
ts->second = strtol(buf, NULL, 10);
goto done;
}
i = 0;
while (i < 3) {
int n;
q = NULL;
n = strtol(p, &q, 10);
if (!q || q == p) {
if (*q == '\0') {
if (i == 0) {
err = 1;
}
goto done;
}
}
if (*q == ':' || *q == '\0' || i == 2) {
switch (i) {
case 0: ts->hour = n; break;
case 1: ts->minute = n; break;
case 2: ts->second = n; break;
}
++i;
if (*q) {
++q;
}
} else {
i = 0;
while (*q && !ISDIGIT(*q)) {
++q;
}
}
p = q;
}
done:
/* final check for overflow */
if (err || ts->hour > 23 || ts->minute > 59 || ts->second > 59) {
return -1;
}
return 0;
}
/**
* Convert string to ODBC TIMESTAMP_STRUCT.
* @param str string to be converted
* @param tss output TIMESTAMP_STRUCT
* @result 0 on success, -1 on error
*
* Strings of the format 'YYYYMMDDhhmmssff' or 'YYYY-MM-DD hh:mm:ss ff'
* or 'YYYY/MM/DD hh:mm:ss ff' or 'hh:mm:ss ff YYYY-MM-DD' are
* converted to a TIMESTAMP_STRUCT. The ISO8601 formats
* YYYY-MM-DDThh:mm:ss[.f]Z
* YYYY-MM-DDThh:mm:ss[.f]shh:mm
* are also supported. In case a time zone field is present,
* the resulting TIMESTAMP_STRUCT is expressed in UTC.
*/
static int
str2timestamp(char *str, TIMESTAMP_STRUCT *tss)
{
int i, m, n, err = 0;
char *p, *q, in = '\0';
tss->year = tss->month = tss->day = 0;
tss->hour = tss->minute = tss->second = 0;
tss->fraction = 0;
p = str;
while (*p && !ISDIGIT(*p)) {
++p;
}
q = p;
i = 0;
while (*q && ISDIGIT(*q)) {
++i;
++q;
}
if (i >= 14) {
char buf[16];
strncpy(buf, p + 0, 4); buf[4] = '\0';
tss->year = strtol(buf, NULL, 10);
strncpy(buf, p + 4, 2); buf[2] = '\0';
tss->month = strtol(buf, NULL, 10);
strncpy(buf, p + 6, 2); buf[2] = '\0';
tss->day = strtol(buf, NULL, 10);
strncpy(buf, p + 8, 2); buf[2] = '\0';
tss->hour = strtol(buf, NULL, 10);
strncpy(buf, p + 10, 2); buf[2] = '\0';
tss->minute = strtol(buf, NULL, 10);
strncpy(buf, p + 12, 2); buf[2] = '\0';
tss->second = strtol(buf, NULL, 10);
if (i > 14) {
m = i - 14;
strncpy(buf, p + 14, m);
while (m < 9) {
buf[m] = '0';
++m;
}
buf[m] = '\0';
tss->fraction = strtol(buf, NULL, 0);
}
m = 7;
goto done;
}
m = i = 0;
while ((m & 7) != 7) {
q = NULL;
n = strtol(p, &q, 10);
if (!q || q == p) {
if (*q == '\0') {
if (m < 1) {
err = 1;
}
goto done;
}
}
if (in == '\0') {
switch (*q) {
case '-':
case '/':
if ((m & 1) == 0) {
in = *q;
i = 0;
}
break;
case ':':
if ((m & 2) == 0) {
in = *q;
i = 0;
}
break;
case ' ':
case '.':
break;
default:
in = '\0';
i = 0;
break;
}
}
switch (in) {
case '-':
case '/':
switch (i) {
case 0: tss->year = n; break;
case 1: tss->month = n; break;
case 2: tss->day = n; break;
}
if (++i >= 3) {
i = 0;
m |= 1;
if (!(m & 2)) {
m |= 8;
}
goto skip;
} else {
++q;
}
break;
case ':':
switch (i) {
case 0: tss->hour = n; break;
case 1: tss->minute = n; break;
case 2: tss->second = n; break;
}
if (++i >= 3) {
i = 0;
m |= 2;
if (*q == '.') {
in = '.';
goto skip2;
}
if (*q == ' ') {
if ((m & 1) == 0) {
char *e = NULL;
int dummy;
dummy = strtol(q + 1, &e, 10);
if (e && *e == '-') {
goto skip;
}
}
in = '.';
goto skip2;
}
goto skip;
} else {
++q;
}
break;
case '.':
if (++i >= 1) {
int ndig = q - p;
if (p[0] == '+' || p[0] == '-') {
ndig--;
}
while (ndig < 9) {
n = n * 10;
++ndig;
}
tss->fraction = n;
m |= 4;
i = 0;
}
default:
skip:
in = '\0';
skip2:
while (*q && !ISDIGIT(*q)) {
++q;
}
}
p = q;
}
if ((m & 7) > 1 && (m & 8)) {
/* ISO8601 timezone */
if (p > str && ISDIGIT(*p)) {
int nn, sign;
q = p - 1;
if (*q != '+' && *q != '-') {
goto done;
}
sign = (*q == '+') ? -1 : 1;
q = NULL;
n = strtol(p, &q, 10);
if (!q || *q++ != ':' || !ISDIGIT(*q)) {
goto done;
}
p = q;
q = NULL;
nn = strtol(p, &q, 0);
tss->minute += nn * sign;
if ((SQLSMALLINT) tss->minute < 0) {
tss->hour -= 1;
tss->minute += 60;
} else if (tss->minute >= 60) {
tss->hour += 1;
tss->minute -= 60;
}
tss->hour += n * sign;
if ((SQLSMALLINT) tss->hour < 0) {
tss->day -= 1;
tss->hour += 24;
} else if (tss->hour >= 24) {
tss->day += 1;
tss->hour -= 24;
}
if ((short) tss->day < 1 || tss->day >= 28) {
int mday, pday, pmon;
mday = getmdays(tss->year, tss->month);
pmon = tss->month - 1;
if (pmon < 1) {
pmon = 12;
}
pday = getmdays(tss->year, pmon);
if ((SQLSMALLINT) tss->day < 1) {
tss->month -= 1;
tss->day = pday;
} else if (tss->day > mday) {
tss->month += 1;
tss->day = 1;
}
if ((SQLSMALLINT) tss->month < 1) {
tss->year -= 1;
tss->month = 12;
} else if (tss->month > 12) {
tss->year += 1;
tss->month = 1;
}
}
}
}
done:
/* Replace missing year/month/day with current date */
if (!err && (m & 1) == 0) {
#ifdef _WIN32
SYSTEMTIME t;
GetLocalTime(&t);
tss->year = t.wYear;
tss->month = t.wMonth;
tss->day = t.wDay;
#else
struct timeval tv;
struct tm tm;
gettimeofday(&tv, NULL);
tm = *localtime(&tv.tv_sec);
tss->year = tm.tm_year + 1900;
tss->month = tm.tm_mon + 1;
tss->day = tm.tm_mday;
#endif
}
/* Normalize fraction */
if (tss->fraction < 0) {
tss->fraction = 0;
}
/* Final check for overflow */
if (err ||
tss->month < 1 || tss->month > 12 ||
tss->day < 1 || tss->day > getmdays(tss->year, tss->month) ||
tss->hour > 23 || tss->minute > 59 || tss->second > 59) {
return -1;
}
return ((m & 7) < 1) ? -1 : 0;
}
/**
* Get boolean flag from string.
* @param string string to be inspected
* @result true or false
*/
static int
getbool(char *string)
{
if (string) {
return string[0] && strchr("Yy123456789Tt", string[0]) != NULL;
}
return 0;
}
#if defined(HAVE_SQLITETRACE) && HAVE_SQLITETRACE
/**
* SQLite trace callback
* @param arg DBC pointer
*/
static void
dbtrace(void *arg, const char *msg)
{
DBC *d = (DBC *) arg;
if (msg && d->trace) {
int len = strlen(msg);
if (len > 0) {
char *end = "\n";
if (msg[len - 1] != ';') {
end = ";\n";
}
fprintf(d->trace, "%s%s", msg, end);
fflush(d->trace);
}
}
}
/**
* Trace function for SQLite return codes
* @param rc SQLite return code
* @param err error string or NULL
*/
static void
dbtracerc(DBC *d, int rc, char *err)
{
if (rc != SQLITE_OK && d->trace) {
fprintf(d->trace, "-- SQLITE ERROR CODE %d", rc);
fprintf(d->trace, err ? ": %s\n" : "\n", err);
fflush(d->trace);
}
}
#else
#define dbtracerc(a,b,c)
#endif
/**
* Open SQLite database file given file name and flags.
* @param d DBC pointer
* @param name file name
* @param dsn data source name
* @param sflag STEPAPI flag
* @param sflag NoTXN flag
* @param busy busy/lock timeout
* @result ODBC error code
*/
static SQLRETURN
dbopen(DBC *d, char *name, char *dsn, char *sflag, char *ntflag, char *busy)
{
char *errp = NULL, *endp = NULL;
int tmp, busyto = 100000;
if (d->sqlite) {
sqlite_close(d->sqlite);
d->sqlite = NULL;
}
d->sqlite = sqlite_open(name, 0, &errp);
if (d->sqlite == NULL) {
connfail:
setstatd(d, -1, errp ? errp : "connect failed",
(*d->ov3) ? "HY000" : "S1000");
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_ERROR;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
#if defined(HAVE_SQLITETRACE) && HAVE_SQLITETRACE
if (d->trace) {
sqlite_trace(d->sqlite, dbtrace, d);
}
#endif
d->step_enable = getbool(sflag);
d->trans_disable = getbool(ntflag);
d->curtype = d->step_enable ?
SQL_CURSOR_FORWARD_ONLY : SQL_CURSOR_STATIC;
tmp = strtol(busy, &endp, 0);
if (endp && *endp == '\0' && endp != busy) {
busyto = tmp;
}
if (busyto < 1 || busyto > 1000000) {
busyto = 1000000;
}
d->timeout = busyto;
if (setsqliteopts(d->sqlite, d) != SQLITE_OK) {
sqlite_close(d->sqlite);
d->sqlite = NULL;
goto connfail;
}
freep(&d->dbname);
d->dbname = xstrdup(name);
freep(&d->dsn);
d->dsn = xstrdup(dsn);
#if defined(_WIN32) || defined(_WIN64)
{
char pname[MAX_PATH];
HANDLE h = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE, GetCurrentProcessId());
pname[0] = '\0';
if (h) {
HMODULE m = NULL, l = LoadLibrary("psapi.dll");
DWORD need;
typedef BOOL (WINAPI *epmfunc)(HANDLE, HMODULE *, DWORD, LPDWORD);
typedef BOOL (WINAPI *gmbfunc)(HANDLE, HMODULE, LPSTR, DWORD);
epmfunc epm;
gmbfunc gmb;
if (l) {
epm = (epmfunc) GetProcAddress(l, "EnumProcessModules");
gmb = (gmbfunc) GetProcAddress(l, "GetModuleBaseNameA");
if (epm && gmb && epm(h, &m, sizeof (m), &need)) {
gmb(h, m, pname, sizeof (pname));
}
FreeLibrary(l);
}
CloseHandle(h);
}
d->xcelqrx = strncasecmp(pname, "EXCEL", 5) == 0 ||
strncasecmp(pname, "MSQRY", 5) == 0;
if (d->trace && d->xcelqrx) {
fprintf(d->trace, "-- enabled EXCEL quirks\n");
fflush(d->trace);
}
}
#endif
return SQL_SUCCESS;
}
/**
* Do one VM step gathering one result row
* @param s statement pointer
* @result ODBC error code
*/
static int
vm_step(STMT *s)
{
DBC *d = (DBC *) s->dbc;
char **rowd = NULL, *errp = NULL;
const char **values, **cols;
int i, ncols, rc;
if (s != d->vm_stmt || !s->vm) {
setstat(s, -1, "stale statement", (*s->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
rc = sqlite_step(s->vm, &ncols, &values, &cols);
if (rc == SQLITE_ROW || rc == SQLITE_DONE) {
++d->vm_rownum;
if (d->vm_rownum == 0 && cols && ncols > 0) {
int size;
char *p;
COL *dyncols;
for (i = size = 0; i < ncols; i++) {
size += 3 + 3 * strlen(cols[i]);
}
dyncols = xmalloc(ncols * sizeof (COL) + size);
if (!dyncols) {
freedyncols(s);
s->ncols = 0;
sqlite_finalize(s->vm, NULL);
s->vm = NULL;
d->vm_stmt = NULL;
return nomem(s);
}
p = (char *) (dyncols + ncols);
for (i = 0; i < ncols; i++) {
char *q;
dyncols[i].db = ((DBC *) (s->dbc))->dbname;
strcpy(p, cols[i]);
dyncols[i].label = p;
p += strlen(p) + 1;
q = strchr(cols[i], '.');
if (q) {
dyncols[i].table = p;
strncpy(p, cols[i], q - cols[i]);
p[q - cols[i]] = '\0';
p += strlen(p) + 1;
strcpy(p, q + 1);
dyncols[i].column = p;
p += strlen(p) + 1;
} else {
dyncols[i].table = "";
strcpy(p, cols[i]);
dyncols[i].column = p;
p += strlen(p) + 1;
}
if (s->longnames) {
dyncols[i].column = dyncols[i].label;
}
#ifdef SQL_LONGVARCHAR
dyncols[i].type = SQL_LONGVARCHAR;
dyncols[i].size = 65535;
#else
dyncols[i].type = SQL_VARCHAR;
dyncols[i].size = 255;
#endif
dyncols[i].index = i;
dyncols[i].scale = 0;
dyncols[i].prec = 0;
dyncols[i].nosign = 1;
dyncols[i].autoinc = SQL_FALSE;
dyncols[i].notnull = SQL_NULLABLE;
dyncols[i].typename = NULL;
}
freedyncols(s);
s->ncols = s->dcols = ncols;
s->dyncols = s->cols = dyncols;
fixupdyncols(s, d->sqlite, cols + ncols);
mkbindcols(s, s->ncols);
}
if (!cols || ncols <= 0) {
goto killvm;
}
if (!values) {
if (rc == SQLITE_DONE) {
freeresult(s, 0);
s->nrows = 0;
sqlite_finalize(s->vm, NULL);
s->vm = NULL;
d->vm_stmt = NULL;
return SQL_SUCCESS;
}
goto killvm;
}
rowd = xmalloc((1 + 2 * ncols) * sizeof (char *));
if (rowd) {
rowd[0] = (char *) ((PTRDIFF_T) (ncols * 2));
++rowd;
for (i = 0; i < ncols; i++) {
rowd[i] = NULL;
rowd[i + ncols] = xstrdup(values[i]);
}
for (i = 0; i < ncols; i++) {
if (values[i] && !rowd[i + ncols]) {
freerows(rowd);
rowd = 0;
break;
}
}
}
if (rowd) {
freeresult(s, 0);
s->nrows = 1;
s->rows = rowd;
s->rowfree = freerows;
if (rc == SQLITE_DONE) {
sqlite_finalize(s->vm, NULL);
s->vm = NULL;
d->vm_stmt = NULL;
}
return SQL_SUCCESS;
}
}
killvm:
sqlite_finalize(s->vm, &errp);
s->vm = NULL;
d->vm_stmt = NULL;
setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", rc);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_ERROR;
}
/**
* Stop running VM
* @param s statement pointer
*/
static void
vm_end(STMT *s)
{
DBC *d;
if (!s || !s->vm) {
return;
}
d = (DBC *) s->dbc;
if (d) {
d->busyint = 0;
}
sqlite_finalize(s->vm, NULL);
s->vm = NULL;
d->vm_stmt = NULL;
}
/**
* Conditionally stop running VM
* @param s statement pointer
*/
static void
vm_end_if(STMT *s)
{
DBC *d = (DBC *) s->dbc;
if (d) {
d->busyint = 0;
}
if (d && d->vm_stmt == s) {
vm_end(s);
}
}
/**
* Start VM for execution of SELECT statement.
* @param s statement pointer
* @param params string array of statement parameters
* @result ODBC error code
*/
static SQLRETURN
vm_start(STMT *s, char **params)
{
DBC *d = (DBC *) s->dbc;
char *errp = NULL, *sql = NULL;
const char *endp;
sqlite_vm *vm;
int rc;
#ifdef CANT_PASS_VALIST_AS_CHARPTR
if (params) {
sql = sqlite_mprintf((char *) s->query,
params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7],
params[8], params[9],
params[10], params[11],
params[12], params[13],
params[14], params[15],
params[16], params[17],
params[18], params[19],
params[20], params[21],
params[22], params[23],
params[24], params[25],
params[26], params[27],
params[28], params[29],
params[30], params[31]);
} else {
sql = sqlite_mprintf((char *) s->query);
}
#else
sql = sqlite_vmprintf((char *) s->query, (char *) params);
#endif
if (!sql) {
return nomem(s);
}
rc = sqlite_compile(d->sqlite, sql, &endp, &vm, &errp);
dbtracerc(d, rc, errp);
sqlite_freemem(sql);
if (rc != SQLITE_OK) {
setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", rc);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_ERROR;
}
s->vm = vm;
d->vm_stmt = s;
d->vm_rownum = -1;
return SQL_SUCCESS;
}
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLBulkOperations(SQLHSTMT stmt, SQLSMALLINT oper)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvunimplstmt(stmt);
HSTMT_UNLOCK(stmt);
return ret;
}
#ifndef WINTERFACE
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLDataSources(SQLHENV env, SQLUSMALLINT dir, SQLCHAR *srvname,
SQLSMALLINT buflen1, SQLSMALLINT *lenp1,
SQLCHAR *desc, SQLSMALLINT buflen2, SQLSMALLINT *lenp2)
{
if (env == SQL_NULL_HENV) {
return SQL_INVALID_HANDLE;
}
return SQL_ERROR;
}
#endif
#ifdef WINTERFACE
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLDataSourcesW(SQLHENV env, SQLUSMALLINT dir, SQLWCHAR *srvname,
SQLSMALLINT buflen1, SQLSMALLINT *lenp1,
SQLWCHAR *desc, SQLSMALLINT buflen2, SQLSMALLINT *lenp2)
{
if (env == SQL_NULL_HENV) {
return SQL_INVALID_HANDLE;
}
return SQL_ERROR;
}
#endif
#ifndef WINTERFACE
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLDrivers(SQLHENV env, SQLUSMALLINT dir, SQLCHAR *drvdesc,
SQLSMALLINT descmax, SQLSMALLINT *desclenp,
SQLCHAR *drvattr, SQLSMALLINT attrmax, SQLSMALLINT *attrlenp)
{
if (env == SQL_NULL_HENV) {
return SQL_INVALID_HANDLE;
}
return SQL_ERROR;
}
#endif
#ifdef WINTERFACE
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLDriversW(SQLHENV env, SQLUSMALLINT dir, SQLWCHAR *drvdesc,
SQLSMALLINT descmax, SQLSMALLINT *desclenp,
SQLWCHAR *drvattr, SQLSMALLINT attrmax, SQLSMALLINT *attrlenp)
{
if (env == SQL_NULL_HENV) {
return SQL_INVALID_HANDLE;
}
return SQL_ERROR;
}
#endif
#ifndef WINTERFACE
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLBrowseConnect(SQLHDBC dbc, SQLCHAR *connin, SQLSMALLINT conninLen,
SQLCHAR *connout, SQLSMALLINT connoutMax,
SQLSMALLINT *connoutLen)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvunimpldbc(dbc);
HDBC_UNLOCK(dbc);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLBrowseConnectW(SQLHDBC dbc, SQLWCHAR *connin, SQLSMALLINT conninLen,
SQLWCHAR *connout, SQLSMALLINT connoutMax,
SQLSMALLINT *connoutLen)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvunimpldbc(dbc);
HDBC_UNLOCK(dbc);
return ret;
}
#endif
/**
* SQLite function "current_time_local" etc.
* @param context SQLite function context
* @param argc number arguments
* @param argv argument vector
*/
static void
time_func(sqlite_func *context, int argc, const char **argv)
{
char buf[128];
PTRDIFF_T what = (PTRDIFF_T) sqlite_user_data(context);
#if defined(_WIN32) || defined(_WIN64)
SYSTEMTIME st;
if (what & 1) {
GetSystemTime(&st);
} else {
GetLocalTime(&st);
}
if (what & 4) {
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
st.wYear, st.wMonth, st.wDay,
st.wHour, st.wMinute, st.wSecond);
} else if (what & 2) {
sprintf(buf, "%04d-%02d-%02d", st.wYear, st.wMonth, st.wDay);
} else {
sprintf(buf, "%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond);
}
#else
time_t t;
struct tm tm;
time(&t);
if (what & 1) {
#ifdef HAVE_GMTIME_R
gmtime_r(&t, &tm);
#else
tm = *gmtime(&t);
#endif
} else {
#ifdef HAVE_LOCALTIME_R
localtime_r(&t, &tm);
#else
tm = *localtime(&t);
#endif
}
if (what & 4) {
sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
} else if (what & 2) {
sprintf(buf, "%04d-%02d-%02d",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
} else {
sprintf(buf, "%02d:%02d:%02d", tm.tm_hour, tm.tm_min, tm.tm_sec);
}
#endif
sqlite_set_result_string(context, buf, -1);
}
#if HAVE_ENCDEC
static const char hexdigits[] = "0123456789ABCDEFabcdef";
/**
* SQLite function "hextobin"
* @param context SQLite function context
* @param argc number arguments
* @param argv argument vector
*/
static void
hextobin_func(sqlite_func *context, int argc, const char **argv)
{
int i, len;
char *bin, *p;
if (argc < 1) {
return;
}
if (!argv[0]) {
sqlite_set_result_string(context, NULL, 4);
return;
}
len = strlen(argv[0]) / 2;
bin = xmalloc(len + 1);
if (!bin) {
oom:
sqlite_set_result_error(context, "out of memory", -1);
return;
}
if (len <= 0) {
sqlite_set_result_string(context, bin, 0);
freep(&bin);
return;
}
for (i = 0, p = (char *) argv[0]; i < len; i++) {
char *x;
int v;
if (!*p || !(x = strchr(hexdigits, *p))) {
goto converr;
}
v = x - hexdigits;
bin[i] = (v >= 16) ? ((v - 6) << 4) : (v << 4);
++p;
if (!*p || !(x = strchr(hexdigits, *p))) {
converr:
freep(&bin);
sqlite_set_result_error(context, "conversion error", -1);
return;
}
v = x - hexdigits;
bin[i] |= (v >= 16) ? (v - 6) : v;
++p;
}
i = sqlite_encode_binary((unsigned char *) bin, len, 0);
p = xmalloc(i + 1);
if (!p) {
freep(&bin);
goto oom;
}
i = sqlite_encode_binary((unsigned char *) bin, len,
(unsigned char *) p);
sqlite_set_result_string(context, p, i);
freep(&bin);
freep(&p);
}
/**
* SQLite function "bintohex"
* @param context SQLite function context
* @param argc number arguments
* @param argv argument vector
*/
static void
bintohex_func(sqlite_func *context, int argc, const char **argv)
{
int i, k, len;
char *bin, *p;
if (argc < 1) {
return;
}
if (!argv[0]) {
empty:
sqlite_set_result_string(context, "", 0);
return;
}
bin = xmalloc(strlen(argv[0]) + 1);
if (!bin) {
oom:
sqlite_set_result_error(context, "out of memory", -1);
return;
}
len = sqlite_decode_binary((unsigned char *) argv[0],
(unsigned char *) bin);
if (len < 0) {
freep(&bin);
sqlite_set_result_error(context, "error decoding binary data", -1);
return;
}
if (len == 0) {
goto empty;
}
p = xmalloc(len * 2 + 1);
if (!p) {
goto oom;
}
for (i = 0, k = 0; i < len; i++) {
p[k++] = hexdigits[(bin[i] >> 4) & 0x0f];
p[k++] = hexdigits[bin[i] & 0x0f];
}
p[k] = '\0';
sqlite_set_result_string(context, p, k);
freep(&bin);
freep(&p);
}
/**
* Encode hex (char) parameter to SQLite binary string.
* @param s STMT pointer
* @param p BINDPARM pointer
* @result ODBD error code
*/
static SQLRETURN
hextobin(STMT *s, BINDPARM *p)
{
int i, len = strlen(p->param) / 2;
char *bin = xmalloc(len + 1), *pp;
if (!bin) {
return nomem(s);
}
if (len <= 0) {
bin[0] = '\0';
freep(&p->parbuf);
p->parbuf = p->param = bin;
p->len = 0;
return SQL_SUCCESS;
}
for (i = 0, pp = (char *) p->param; i < len; i++) {
char *x;
int v;
if (!*pp || !(x = strchr(hexdigits, *pp))) {
goto converr;
}
v = x - hexdigits;
bin[i] = (v >= 16) ? ((v - 6) << 4) : (v << 4);
++pp;
if (!*pp || !(x = strchr(hexdigits, *pp))) {
converr:
freep(&bin);
setstat(s, -1, "conversion error", (*s->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
v = x - hexdigits;
bin[i] |= (v >= 16) ? (v - 6) : v;
++pp;
}
i = sqlite_encode_binary((unsigned char *) bin, len, 0);
pp = xmalloc(i + 1);
if (!pp) {
freep(&bin);
return nomem(s);
}
p->len = sqlite_encode_binary((unsigned char *) bin, len,
(unsigned char *) pp);
freep(&p->parbuf);
p->parbuf = p->param = pp;
freep(&bin);
return SQL_SUCCESS;
}
#endif
/**
* Internal put (partial) parameter data into executing statement.
* @param stmt statement handle
* @param data pointer to data
* @param len length of data
* @result ODBC error code
*/
static SQLRETURN
drvputdata(SQLHSTMT stmt, SQLPOINTER data, SQLLEN len)
{
STMT *s;
int i, dlen, done = 0;
BINDPARM *p;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (!s->query || s->nparams <= 0) {
seqerr:
setstat(s, -1, "sequence error", "HY010");
return SQL_ERROR;
}
for (i = 0; i < s->nparams; i++) {
p = &s->bindparms[i];
if (p->need > 0) {
int type = mapdeftype(p->type, p->stype, -1, s->nowchar[0]);
if (len == SQL_NULL_DATA) {
freep(&p->parbuf);
p->param = NULL;
p->len = SQL_NULL_DATA;
p->need = -1;
} else if (type != SQL_C_CHAR
#ifdef WINTERFACE
&& type != SQL_C_WCHAR
#endif
#if HAVE_ENCDEC
&& type != SQL_C_BINARY
#endif
) {
int size = 0;
switch (type) {
case SQL_C_TINYINT:
case SQL_C_UTINYINT:
case SQL_C_STINYINT:
#ifdef SQL_BIT
case SQL_C_BIT:
#endif
size = sizeof (char);
break;
case SQL_C_SHORT:
case SQL_C_USHORT:
case SQL_C_SSHORT:
size = sizeof (short);
break;
case SQL_C_LONG:
case SQL_C_ULONG:
case SQL_C_SLONG:
size = sizeof (long);
break;
case SQL_C_FLOAT:
size = sizeof (float);
break;
case SQL_C_DOUBLE:
size = sizeof (double);
break;
#ifdef SQL_C_TYPE_DATE
case SQL_C_TYPE_DATE:
#endif
case SQL_C_DATE:
size = sizeof (DATE_STRUCT);
break;
#ifdef SQL_C_TYPE_DATE
case SQL_C_TYPE_TIME:
#endif
case SQL_C_TIME:
size = sizeof (TIME_STRUCT);
break;
#ifdef SQL_C_TYPE_DATE
case SQL_C_TYPE_TIMESTAMP:
#endif
case SQL_C_TIMESTAMP:
size = sizeof (TIMESTAMP_STRUCT);
break;
}
freep(&p->parbuf);
p->parbuf = xmalloc(size);
if (!p->parbuf) {
return nomem(s);
}
p->param = p->parbuf;
memcpy(p->param, data, size);
p->len = size;
p->need = -1;
} else if (len == SQL_NTS && (
type == SQL_C_CHAR
#ifdef WINTERFACE
|| type == SQL_C_WCHAR
#endif
)) {
char *dp = data;
#ifdef WINTERFACE
if (type == SQL_C_WCHAR) {
dp = uc_to_utf(data, len);
if (!dp) {
return nomem(s);
}
}
#endif
dlen = strlen(dp);
freep(&p->parbuf);
p->parbuf = xmalloc(dlen + 1);
if (!p->parbuf) {
#ifdef WINTERFACE
if (dp != data) {
uc_free(dp);
}
#endif
return nomem(s);
}
p->param = p->parbuf;
strcpy(p->param, dp);
#ifdef WINTERFACE
if (dp != data) {
uc_free(dp);
}
#endif
p->len = dlen;
p->need = -1;
} else if (len < 0) {
setstat(s, -1, "invalid length", "HY090");
return SQL_ERROR;
} else {
dlen = min(p->len - p->offs, len);
if (!p->param) {
setstat(s, -1, "no memory for parameter", "HY013");
return SQL_ERROR;
}
memcpy((char *) p->param + p->offs, data, dlen);
p->offs += dlen;
if (p->offs >= p->len) {
#ifdef WINTERFACE
if (type == SQL_C_WCHAR) {
char *dp = uc_to_utf(p->param, p->len);
char *np;
int nlen;
if (!dp) {
return nomem(s);
}
nlen = strlen(dp);
np = xmalloc(nlen + 1);
if (!np) {
uc_free(dp);
return nomem(s);
}
strcpy(np, dp);
uc_free(dp);
if (p->param == p->parbuf) {
freep(&p->parbuf);
}
p->parbuf = p->param = np;
p->len = nlen;
} else {
*((char *) p->param + p->len) = '\0';
}
#else
#if defined(_WIN32) || defined(_WIN64)
if (p->type == SQL_C_WCHAR &&
(p->stype == SQL_VARCHAR ||
p->stype == SQL_LONGVARCHAR) &&
p->len == p->coldef * sizeof (SQLWCHAR)) {
/* fix for MS-Access */
p->len = p->coldef;
}
#endif
*((char *) p->param + p->len) = '\0';
#endif
#if HAVE_ENCDEC
if ((p->stype == SQL_BINARY ||
p->stype == SQL_VARBINARY ||
p->stype == SQL_LONGVARBINARY) &&
#ifdef WINTERFACE
(type == SQL_C_CHAR || type == SQL_C_WCHAR)
#else
type == SQL_C_CHAR
#endif
) {
if (hextobin(s, p) != SQL_SUCCESS) {
return SQL_ERROR;
}
} else if (type == SQL_C_BINARY) {
int bsize;
unsigned char *bin;
bsize = sqlite_encode_binary(p->param, p->len, 0);
bin = xmalloc(bsize + 1);
if (!bin) {
return nomem(s);
}
p->len = sqlite_encode_binary(p->param, p->len, bin);
if (p->param == p->parbuf) {
freep(&p->parbuf);
}
p->parbuf = p->param = bin;
}
#endif
p->need = -1;
}
}
done = 1;
break;
}
}
if (!done) {
goto seqerr;
}
return SQL_SUCCESS;
}
/**
* Put (partial) parameter data into executing statement.
* @param stmt statement handle
* @param data pointer to data
* @param len length of data
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLPutData(SQLHSTMT stmt, SQLPOINTER data, SQLLEN len)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvputdata(stmt, data, len);
HSTMT_UNLOCK(stmt);
return ret;
}
/**
* Clear out parameter bindings, if any.
* @param s statement pointer
*/
static SQLRETURN
freeparams(STMT *s)
{
if (s->bindparms) {
int n;
for (n = 0; n < s->nbindparms; n++) {
freep(&s->bindparms[n].parbuf);
memset(&s->bindparms[n], 0, sizeof (BINDPARM));
}
}
return SQL_SUCCESS;
}
/**
* Substitute parameter for statement.
* @param s statement pointer
* @param pnum parameter number
* @param outp output pointer or NULL
* @result ODBC error code
*
* If no output buffer is given, the function computes and
* reports the space needed for the parameter. Otherwise
* the parameter is converted to its string representation
* in order to be presented to sqlite_exec_vprintf() et.al.
*/
static SQLRETURN
substparam(STMT *s, int pnum, char **outp)
{
char *outdata = NULL;
int type, len, isnull = 0, needalloc = 0;
BINDPARM *p;
double dval;
#if HAVE_ENCDEC
int chkbin = 1;
#endif
if (!s->bindparms || pnum < 0 || pnum >= s->nbindparms) {
goto error;
}
p = &s->bindparms[pnum];
type = mapdeftype(p->type, p->stype, -1, s->nowchar[0]);
if (p->need > 0) {
return setupparbuf(s, p);
}
p->strbuf[0] = '\0';
if (!p->param || (p->lenp && *p->lenp == SQL_NULL_DATA)) {
isnull = 1;
goto bind;
}
switch (type) {
case SQL_C_CHAR:
#ifdef WINTERFACE
case SQL_C_WCHAR:
#endif
#if HAVE_ENCDEC
case SQL_C_BINARY:
#endif
break;
#ifdef SQL_BIT
case SQL_C_BIT:
strcpy(p->strbuf, (*((unsigned char *) p->param)) ? "1" : "0");
goto bind;
#endif
case SQL_C_UTINYINT:
sprintf(p->strbuf, "%d", *((unsigned char *) p->param));
goto bind;
case SQL_C_TINYINT:
case SQL_C_STINYINT:
sprintf(p->strbuf, "%d", *((char *) p->param));
goto bind;
case SQL_C_USHORT:
sprintf(p->strbuf, "%d", *((unsigned short *) p->param));
goto bind;
case SQL_C_SHORT:
case SQL_C_SSHORT:
sprintf(p->strbuf, "%d", *((short *) p->param));
goto bind;
case SQL_C_ULONG:
case SQL_C_LONG:
case SQL_C_SLONG:
sprintf(p->strbuf, "%ld", *((long *) p->param));
goto bind;
case SQL_C_FLOAT:
dval = *((float *) p->param);
goto dodouble;
case SQL_C_DOUBLE:
dval = *((double *) p->param);
dodouble:
#if defined(HAVE_SQLITEMPRINTF) && HAVE_SQLITEMPRINTF
{
char *buf2 = sqlite_mprintf("%.16g", dval);
if (buf2) {
strcpy(p->strbuf, buf2);
sqlite_freemem(buf2);
} else {
isnull = 1;
}
}
#else
ln_sprintfg(p->strbuf, dval);
#endif
goto bind;
#ifdef SQL_C_TYPE_DATE
case SQL_C_TYPE_DATE:
#endif
case SQL_C_DATE:
sprintf(p->strbuf, "%04d-%02d-%02d",
((DATE_STRUCT *) p->param)->year,
((DATE_STRUCT *) p->param)->month,
((DATE_STRUCT *) p->param)->day);
goto bind;
#ifdef SQL_C_TYPE_TIME
case SQL_C_TYPE_TIME:
#endif
case SQL_C_TIME:
sprintf(p->strbuf, "%02d:%02d:%02d",
((TIME_STRUCT *) p->param)->hour,
((TIME_STRUCT *) p->param)->minute,
((TIME_STRUCT *) p->param)->second);
goto bind;
#ifdef SQL_C_TYPE_TIMESTAMP
case SQL_C_TYPE_TIMESTAMP:
#endif
case SQL_C_TIMESTAMP:
len = (int) ((TIMESTAMP_STRUCT *) p->param)->fraction;
len /= 1000000;
len = len % 1000;
if (len < 0) {
len = 0;
}
sprintf(p->strbuf, "%04d-%02d-%02d %02d:%02d:%02d.%03d",
((TIMESTAMP_STRUCT *) p->param)->year,
((TIMESTAMP_STRUCT *) p->param)->month,
((TIMESTAMP_STRUCT *) p->param)->day,
((TIMESTAMP_STRUCT *) p->param)->hour,
((TIMESTAMP_STRUCT *) p->param)->minute,
((TIMESTAMP_STRUCT *) p->param)->second,
len);
bind:
if (outp) {
*outp = isnull ? NULL : p->strbuf;
}
return SQL_SUCCESS;
default:
error:
setstat(s, -1, "unsupported parameter type",
(*s->ov3) ? "07009" : "S1093");
return SQL_ERROR;
}
if (!p->parbuf && p->lenp) {
#ifdef WINTERFACE
if (type == SQL_C_WCHAR) {
if (*p->lenp == SQL_NTS) {
p->max = uc_strlen(p->param) * sizeof (SQLWCHAR);
} else if (*p->lenp >= 0) {
p->max = *p->lenp;
}
} else
#endif
if (type == SQL_C_CHAR) {
if (*p->lenp == SQL_NTS) {
p->len = p->max = strlen(p->param);
} else if (*p->lenp >= 0) {
p->len = p->max = *p->lenp;
needalloc = 1;
}
}
#if HAVE_ENCDEC
else if (type == SQL_C_BINARY) {
p->len = p->max = *p->lenp;
}
#endif
}
if (p->need < 0 && p->parbuf == p->param) {
outdata = p->param;
goto putp;
}
#ifdef WINTERFACE
if (type == SQL_C_WCHAR) {
char *dp = uc_to_utf(p->param, p->max);
if (!dp) {
return nomem(s);
}
if (p->param == p->parbuf) {
freep(&p->parbuf);
}
p->parbuf = p->param = dp;
p->len = strlen(p->param);
outdata = p->param;
} else
#endif
#if HAVE_ENCDEC
if (type == SQL_C_BINARY) {
int bsize;
char *dp;
p->len = *p->lenp;
if (p->len < 0) {
setstat(s, -1, "invalid length reference", "HY009");
return SQL_ERROR;
}
bsize = sqlite_encode_binary(p->param, p->len, 0);
dp = xmalloc(bsize + 1);
if (!dp) {
return nomem(s);
}
p->len = sqlite_encode_binary(p->param, p->len, (unsigned char *) dp);
if (p->param == p->parbuf) {
freep(&p->parbuf);
}
p->parbuf = p->param = dp;
chkbin = 0;
outdata = p->param;
} else
#endif
if (type == SQL_C_CHAR) {
outdata = p->param;
if (needalloc) {
char *dp;
freep(&p->parbuf);
dp = xmalloc(p->len + 1);
if (!dp) {
return nomem(s);
}
memcpy(dp, p->param, p->len);
dp[p->len] = '\0';
p->parbuf = p->param = dp;
outdata = p->param;
}
} else {
outdata = p->param;
#if HAVE_ENCDEC
chkbin = 0;
#endif
}
#if HAVE_ENCDEC
if (chkbin) {
if (p->stype == SQL_BINARY ||
p->stype == SQL_VARBINARY ||
p->stype == SQL_LONGVARBINARY) {
if (hextobin(s, p) != SQL_SUCCESS) {
return SQL_ERROR;
}
outdata = p->param;
}
}
#endif
putp:
if (outp) {
*outp = outdata;
}
return SQL_SUCCESS;
}
/**
* Internal bind parameter on HSTMT.
* @param stmt statement handle
* @param pnum parameter number, starting at 1
* @param iotype input/output type of parameter
* @param buftype type of host variable
* @param ptype
* @param coldef
* @param scale
* @param data pointer to host variable
* @param buflen length of host variable
* @param len output length pointer
* @result ODBC error code
*/
static SQLRETURN
drvbindparam(SQLHSTMT stmt, SQLUSMALLINT pnum, SQLSMALLINT iotype,
SQLSMALLINT buftype, SQLSMALLINT ptype, SQLUINTEGER coldef,
SQLSMALLINT scale,
SQLPOINTER data, SQLINTEGER buflen, SQLLEN *len)
{
STMT *s;
BINDPARM *p;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (pnum == 0) {
setstat(s, -1, "invalid parameter", (*s->ov3) ? "07009" : "S1093");
return SQL_ERROR;
}
if (!data && (!len || (*len != SQL_NULL_DATA &&
*len > SQL_LEN_DATA_AT_EXEC_OFFSET))) {
setstat(s, -1, "invalid buffer", "HY003");
return SQL_ERROR;
}
if (len && *len < 0 && *len > SQL_LEN_DATA_AT_EXEC_OFFSET &&
*len != SQL_NTS && *len != SQL_NULL_DATA) {
setstat(s, -1, "invalid length reference", "HY009");
return SQL_ERROR;
}
--pnum;
if (s->bindparms) {
if (pnum >= s->nbindparms) {
BINDPARM *newparms;
newparms = xrealloc(s->bindparms,
(pnum + 1) * sizeof (BINDPARM));
if (!newparms) {
outofmem:
return nomem(s);
}
s->bindparms = newparms;
memset(&s->bindparms[s->nbindparms], 0,
(pnum + 1 - s->nbindparms) * sizeof (BINDPARM));
s->nbindparms = pnum + 1;
}
} else {
int npar = max(10, pnum + 1);
s->bindparms = xmalloc(npar * sizeof (BINDPARM));
if (!s->bindparms) {
goto outofmem;
}
memset(s->bindparms, 0, npar * sizeof (BINDPARM));
s->nbindparms = npar;
}
p = &s->bindparms[pnum];
p->type = buftype;
p->stype = ptype;
p->coldef = coldef;
p->scale = scale;
p->max = buflen;
p->inc = buflen;
p->lenp0 = p->lenp = len;
p->offs = 0;
p->len = 0;
p->param0 = data;
freep(&p->parbuf);
p->param = p->param0;
p->bound = 1;
p->need = 0;
if (p->lenp && *p->lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET) {
p->need = 1;
}
return SQL_SUCCESS;
}
/**
* Bind parameter on HSTMT.
* @param stmt statement handle
* @param pnum parameter number, starting at 1
* @param iotype input/output type of parameter
* @param buftype type of host variable
* @param ptype
* @param coldef
* @param scale
* @param data pointer to host variable
* @param buflen length of host variable
* @param len output length pointer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLBindParameter(SQLHSTMT stmt, SQLUSMALLINT pnum, SQLSMALLINT iotype,
SQLSMALLINT buftype, SQLSMALLINT ptype, SQLULEN coldef,
SQLSMALLINT scale,
SQLPOINTER data, SQLLEN buflen, SQLLEN *len)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvbindparam(stmt, pnum, iotype, buftype, ptype, coldef,
scale, data, buflen, len);
HSTMT_UNLOCK(stmt);
return ret;
}
/**
* Bind parameter on HSTMT.
* @param stmt statement handle
* @param pnum parameter number, starting at 1
* @param vtype input/output type of parameter
* @param ptype
* @param lenprec
* @param scale
* @param val pointer to host variable
* @param lenp output length pointer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLBindParam(SQLHSTMT stmt, SQLUSMALLINT pnum, SQLSMALLINT vtype,
SQLSMALLINT ptype, SQLULEN lenprec,
SQLSMALLINT scale, SQLPOINTER val,
SQLLEN *lenp)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvbindparam(stmt, pnum, SQL_PARAM_INPUT, vtype, ptype,
lenprec, scale, val, 0, lenp);
HSTMT_UNLOCK(stmt);
return ret;
}
/**
* Return number of parameters.
* @param stmt statement handle
* @param nparam output parameter count
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLNumParams(SQLHSTMT stmt, SQLSMALLINT *nparam)
{
STMT *s;
SQLSMALLINT dummy;
HSTMT_LOCK(stmt);
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (!nparam) {
nparam = &dummy;
}
*nparam = s->nparams;
HSTMT_UNLOCK(stmt);
return SQL_SUCCESS;
}
/**
* Setup parameter buffer for deferred parameter.
* @param s pointer to STMT
* @param p pointer to BINDPARM
* @result ODBC error code (success indicated by SQL_NEED_DATA)
*/
static SQLRETURN
setupparbuf(STMT *s, BINDPARM *p)
{
if (!p->parbuf) {
p->len = SQL_LEN_DATA_AT_EXEC(*p->lenp);
if (p->len < 0 && p->len != SQL_NTS &&
p->len != SQL_NULL_DATA) {
setstat(s, -1, "invalid length", "HY009");
return SQL_ERROR;
}
if (p->len >= 0) {
p->parbuf = xmalloc(p->len + 1);
if (!p->parbuf) {
return nomem(s);
}
p->param = p->parbuf;
} else {
p->param = NULL;
}
}
return SQL_NEED_DATA;
}
/**
* Retrieve next parameter for sending data to executing query.
* @param stmt statement handle
* @param pind pointer to output parameter indicator
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLParamData(SQLHSTMT stmt, SQLPOINTER *pind)
{
STMT *s;
int i;
SQLPOINTER dummy;
SQLRETURN ret;
HSTMT_LOCK(stmt);
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (!pind) {
pind = &dummy;
}
for (i = 0; i < s->nparams; i++) {
BINDPARM *p = &s->bindparms[i];
if (p->need > 0) {
*pind = (SQLPOINTER) p->param0;
ret = setupparbuf(s, p);
goto done;
}
}
ret = drvexecute(stmt, 0);
done:
HSTMT_UNLOCK(stmt);
return ret;
}
/**
* Return information about parameter.
* @param stmt statement handle
* @param pnum parameter number, starting at 1
* @param dtype output type indicator
* @param size output size indicator
* @param decdigits output number of digits
* @param nullable output NULL allowed indicator
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLDescribeParam(SQLHSTMT stmt, SQLUSMALLINT pnum, SQLSMALLINT *dtype,
SQLULEN *size, SQLSMALLINT *decdigits, SQLSMALLINT *nullable)
{
STMT *s;
SQLRETURN ret = SQL_ERROR;
HSTMT_LOCK(stmt);
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
--pnum;
if (pnum >= s->nparams) {
setstat(s, -1, "invalid parameter index",
(*s->ov3) ? "HY000" : "S1000");
goto done;
}
if (dtype) {
#ifdef SQL_LONGVARCHAR
#ifdef WINTERFACE
*dtype = s->nowchar[0] ? SQL_LONGVARCHAR : SQL_WLONGVARCHAR;
#else
*dtype = SQL_LONGVARCHAR;
#endif
#else
#ifdef WINTERFACE
*dtype = s->nowchar[0] ? SQL_VARCHAR : SQL_WVARCHAR;
#else
*dtype = SQL_VARCHAR;
#endif
#endif
}
if (size) {
#ifdef SQL_LONGVARCHAR
*size = 65536;
#else
*size = 255;
#endif
}
if (decdigits) {
*decdigits = 0;
}
if (nullable) {
*nullable = SQL_NULLABLE;
}
ret = SQL_SUCCESS;
done:
HSTMT_UNLOCK(stmt);
return ret;
}
/**
* Set information on parameter.
* @param stmt statement handle
* @param par parameter number, starting at 1
* @param type type of host variable
* @param sqltype
* @param coldef
* @param scale
* @param val pointer to host variable
* @param len output length pointer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSetParam(SQLHSTMT stmt, SQLUSMALLINT par, SQLSMALLINT type,
SQLSMALLINT sqltype, SQLULEN coldef,
SQLSMALLINT scale, SQLPOINTER val, SQLLEN *nval)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvbindparam(stmt, par, SQL_PARAM_INPUT,
type, sqltype, coldef, scale, val,
SQL_SETPARAM_VALUE_MAX, nval);
HSTMT_UNLOCK(stmt);
return ret;
}
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLParamOptions(SQLHSTMT stmt, SQLULEN rows, SQLULEN *rowp)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvunimplstmt(stmt);
HSTMT_UNLOCK(stmt);
return ret;
}
#ifndef WINTERFACE
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLGetDescField(SQLHDESC handle, SQLSMALLINT recno,
SQLSMALLINT fieldid, SQLPOINTER value,
SQLINTEGER buflen, SQLINTEGER *strlen)
{
return SQL_ERROR;
}
#endif
#ifdef WINTERFACE
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLGetDescFieldW(SQLHDESC handle, SQLSMALLINT recno,
SQLSMALLINT fieldid, SQLPOINTER value,
SQLINTEGER buflen, SQLINTEGER *strlen)
{
return SQL_ERROR;
}
#endif
#ifndef WINTERFACE
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLSetDescField(SQLHDESC handle, SQLSMALLINT recno,
SQLSMALLINT fieldid, SQLPOINTER value,
SQLINTEGER buflen)
{
return SQL_ERROR;
}
#endif
#ifdef WINTERFACE
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLSetDescFieldW(SQLHDESC handle, SQLSMALLINT recno,
SQLSMALLINT fieldid, SQLPOINTER value,
SQLINTEGER buflen)
{
return SQL_ERROR;
}
#endif
#ifndef WINTERFACE
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLGetDescRec(SQLHDESC handle, SQLSMALLINT recno,
SQLCHAR *name, SQLSMALLINT buflen,
SQLSMALLINT *strlen, SQLSMALLINT *type,
SQLSMALLINT *subtype, SQLLEN *len,
SQLSMALLINT *prec, SQLSMALLINT *scale,
SQLSMALLINT *nullable)
{
return SQL_ERROR;
}
#endif
#ifdef WINTERFACE
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLGetDescRecW(SQLHDESC handle, SQLSMALLINT recno,
SQLWCHAR *name, SQLSMALLINT buflen,
SQLSMALLINT *strlen, SQLSMALLINT *type,
SQLSMALLINT *subtype, SQLLEN *len,
SQLSMALLINT *prec, SQLSMALLINT *scale,
SQLSMALLINT *nullable)
{
return SQL_ERROR;
}
#endif
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLSetDescRec(SQLHDESC handle, SQLSMALLINT recno,
SQLSMALLINT type, SQLSMALLINT subtype,
SQLLEN len, SQLSMALLINT prec,
SQLSMALLINT scale, SQLPOINTER data,
SQLLEN *strlen, SQLLEN *indicator)
{
return SQL_ERROR;
}
/**
* Setup empty result set from constant column specification.
* @param stmt statement handle
* @param colspec column specification array (default, ODBC2)
* @param ncols number of columns (default, ODBC2)
* @param colspec3 column specification array (ODBC3)
* @param ncols3 number of columns (ODBC3)
* @param nret returns number of columns
* @result ODBC error code
*/
static SQLRETURN
mkresultset(HSTMT stmt, COL *colspec, int ncols, COL *colspec3,
int ncols3, int *nret)
{
STMT *s;
DBC *d;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (s->dbc == SQL_NULL_HDBC) {
noconn:
return noconn(s);
}
d = (DBC *) s->dbc;
if (!d->sqlite) {
goto noconn;
}
vm_end_if(s);
freeresult(s, 0);
if (colspec3 && *s->ov3) {
s->ncols = ncols3;
s->cols = colspec3;
} else {
s->ncols = ncols;
s->cols = colspec;
}
mkbindcols(s, s->ncols);
s->nowchar[1] = 1;
s->nrows = 0;
s->rowp = -1;
s->isselect = -1;
if (nret) {
*nret = s->ncols;
}
return SQL_SUCCESS;
}
/**
* Columns for result set of SQLTablePrivileges().
*/
static COL tablePrivSpec2[] = {
{ "SYSTEM", "TABLEPRIV", "TABLE_QUALIFIER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TABLEPRIV", "TABLE_OWNER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TABLEPRIV", "TABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "TABLEPRIV", "GRANTOR", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TABLEPRIV", "GRANTEE", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TABLEPRIV", "PRIVILEGE", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TABLEPRIV", "IS_GRANTABLE", SCOL_VARCHAR, 50 }
};
static COL tablePrivSpec3[] = {
{ "SYSTEM", "TABLEPRIV", "TABLE_CAT", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TABLEPRIV", "TABLE_SCHEM", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TABLEPRIV", "TABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "TABLEPRIV", "GRANTOR", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TABLEPRIV", "GRANTEE", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TABLEPRIV", "PRIVILEGE", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TABLEPRIV", "IS_GRANTABLE", SCOL_VARCHAR, 50 }
};
#if !defined(WINTERFACE) || (defined(HAVE_UNIXODBC) && HAVE_UNIXODBC)
/**
* Retrieve privileges on tables and/or views.
* @param stmt statement handle
* @param catalog catalog name/pattern or NULL
* @param catalogLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLTablePrivileges(SQLHSTMT stmt,
SQLCHAR *catalog, SQLSMALLINT catalogLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *table, SQLSMALLINT tableLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = mkresultset(stmt, tablePrivSpec2, array_size(tablePrivSpec2),
tablePrivSpec3, array_size(tablePrivSpec3), NULL);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#if !defined(HAVE_UNIXODBC) || !HAVE_UNIXODBC
#ifdef WINTERFACE
/**
* Retrieve privileges on tables and/or views (UNICODE version).
* @param stmt statement handle
* @param catalog catalog name/pattern or NULL
* @param catalogLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLTablePrivilegesW(SQLHSTMT stmt,
SQLWCHAR *catalog, SQLSMALLINT catalogLen,
SQLWCHAR *schema, SQLSMALLINT schemaLen,
SQLWCHAR *table, SQLSMALLINT tableLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = mkresultset(stmt, tablePrivSpec2, array_size(tablePrivSpec2),
tablePrivSpec3, array_size(tablePrivSpec3), NULL);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#endif
/**
* Columns for result set of SQLColumnPrivileges().
*/
static COL colPrivSpec2[] = {
{ "SYSTEM", "COLPRIV", "TABLE_QUALIFIER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLPRIV", "TABLE_OWNER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLPRIV", "TABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "COLPRIV", "COLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "COLPRIV", "GRANTOR", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLPRIV", "GRANTEE", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLPRIV", "PRIVILEGE", SCOL_VARCHAR, 50 }
};
static COL colPrivSpec3[] = {
{ "SYSTEM", "COLPRIV", "TABLE_CAT", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLPRIV", "TABLE_SCHEM", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLPRIV", "TABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "COLPRIV", "COLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "COLPRIV", "GRANTOR", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLPRIV", "GRANTEE", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLPRIV", "PRIVILEGE", SCOL_VARCHAR, 50 }
};
#if !defined(WINTERFACE) || (defined(HAVE_UNIXODBC) && HAVE_UNIXODBC)
/**
* Retrieve privileges on columns.
* @param stmt statement handle
* @param catalog catalog name/pattern or NULL
* @param catalogLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param column column name or NULL
* @param columnLen length of column name or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLColumnPrivileges(SQLHSTMT stmt,
SQLCHAR *catalog, SQLSMALLINT catalogLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *table, SQLSMALLINT tableLen,
SQLCHAR *column, SQLSMALLINT columnLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = mkresultset(stmt, colPrivSpec2, array_size(colPrivSpec2),
colPrivSpec3, array_size(colPrivSpec3), NULL);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#if !defined(HAVE_UNIXODBC) || !HAVE_UNIXODBC
#ifdef WINTERFACE
/**
* Retrieve privileges on columns (UNICODE version).
* @param stmt statement handle
* @param catalog catalog name/pattern or NULL
* @param catalogLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param column column name or NULL
* @param columnLen length of column name or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLColumnPrivilegesW(SQLHSTMT stmt,
SQLWCHAR *catalog, SQLSMALLINT catalogLen,
SQLWCHAR *schema, SQLSMALLINT schemaLen,
SQLWCHAR *table, SQLSMALLINT tableLen,
SQLWCHAR *column, SQLSMALLINT columnLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = mkresultset(stmt, colPrivSpec2, array_size(colPrivSpec2),
colPrivSpec3, array_size(colPrivSpec3), NULL);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#endif
/**
* Columns for result set of SQLPrimaryKeys().
*/
static COL pkeySpec2[] = {
{ "SYSTEM", "PRIMARYKEY", "TABLE_QUALIFIER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PRIMARYKEY", "TABLE_OWNER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PRIMARYKEY", "TABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "PRIMARYKEY", "COLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "PRIMARYKEY", "KEY_SEQ", SQL_SMALLINT, 50 },
{ "SYSTEM", "PRIMARYKEY", "PK_NAME", SCOL_VARCHAR, 50 }
};
static COL pkeySpec3[] = {
{ "SYSTEM", "PRIMARYKEY", "TABLE_CAT", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PRIMARYKEY", "TABLE_SCHEM", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PRIMARYKEY", "TABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "PRIMARYKEY", "COLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "PRIMARYKEY", "KEY_SEQ", SQL_SMALLINT, 50 },
{ "SYSTEM", "PRIMARYKEY", "PK_NAME", SCOL_VARCHAR, 50 }
};
/**
* Internal retrieve information about indexed columns.
* @param stmt statement handle
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @result ODBC error code
*/
static SQLRETURN
drvprimarykeys(SQLHSTMT stmt,
SQLCHAR *cat, SQLSMALLINT catLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *table, SQLSMALLINT tableLen)
{
STMT *s;
DBC *d;
SQLRETURN sret;
int i, asize, ret, nrows, ncols, nrows2 = 0, ncols2 = 0;
int namec = -1, uniquec = -1, namec2 = -1, uniquec2 = -1, offs, seq = 1;
PTRDIFF_T size;
char **rowp = NULL, **rowp2 = NULL, *errp = NULL, tname[512];
sret = mkresultset(stmt, pkeySpec2, array_size(pkeySpec2),
pkeySpec3, array_size(pkeySpec3), &asize);
if (sret != SQL_SUCCESS) {
return sret;
}
s = (STMT *) stmt;
d = (DBC *) s->dbc;
if (!table || table[0] == '\0' || table[0] == '%') {
setstat(s, -1, "need table name", (*s->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
if (tableLen == SQL_NTS) {
size = sizeof (tname) - 1;
} else {
size = min(sizeof (tname) - 1, tableLen);
}
strncpy(tname, (char *) table, size);
tname[size] = '\0';
unescpat(tname);
sret = starttran(s);
if (sret != SQL_SUCCESS) {
return sret;
}
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA table_info('%q')", &rowp,
&nrows, &ncols, &errp, tname);
if (ret != SQLITE_OK) {
setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", ret);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_ERROR;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
size = 0;
if (ncols * nrows > 0) {
int typec;
namec = findcol(rowp, ncols, "name");
uniquec = findcol(rowp, ncols, "pk");
typec = findcol(rowp, ncols, "type");
if (namec >= 0 && uniquec >= 0 && typec >= 0) {
for (i = 1; i <= nrows; i++) {
if (*rowp[i * ncols + uniquec] != '0') {
size++;
}
}
}
}
if (size == 0) {
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA index_list('%q')", &rowp2,
&nrows2, &ncols2, &errp, tname);
if (ret != SQLITE_OK) {
sqlite_free_table(rowp);
sqlite_free_table(rowp2);
setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", ret);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_ERROR;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
}
if (ncols2 * nrows2 > 0) {
namec2 = findcol(rowp2, ncols, "name");
uniquec2 = findcol(rowp2, ncols, "unique");
if (namec2 >= 0 && uniquec2 >= 0) {
for (i = 1; i <= nrows2; i++) {
int nnrows, nncols;
char **rowpp;
if (*rowp2[i * ncols2 + namec2] != '(' ||
!strstr(rowp2[i * ncols2 + namec2], " autoindex ")) {
continue;
}
if (*rowp2[i * ncols2 + uniquec2] != '0') {
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA index_info('%q')",
&rowpp, &nnrows, &nncols,
NULL,
rowp2[i * ncols2 + namec2]);
if (ret == SQLITE_OK) {
size += nnrows;
sqlite_free_table(rowpp);
}
}
}
}
}
if (size == 0) {
sqlite_free_table(rowp);
sqlite_free_table(rowp2);
return SQL_SUCCESS;
}
s->nrows = size;
size = (size + 1) * asize;
s->rows = xmalloc((size + 1) * sizeof (char *));
if (!s->rows) {
s->nrows = 0;
sqlite_free_table(rowp);
sqlite_free_table(rowp2);
return nomem(s);
}
s->rows[0] = (char *) size;
s->rows += 1;
memset(s->rows, 0, sizeof (char *) * size);
s->rowfree = freerows;
offs = s->ncols;
if (rowp) {
for (i = 1; i <= nrows; i++) {
if (*rowp[i * ncols + uniquec] != '0') {
char buf[32];
s->rows[offs + 0] = xstrdup("");
#if defined(_WIN32) || defined(_WIN64)
s->rows[offs + 1] = xstrdup(d->xcelqrx ? "main" : "");
#else
s->rows[offs + 1] = xstrdup("");
#endif
s->rows[offs + 2] = xstrdup(tname);
s->rows[offs + 3] = xstrdup(rowp[i * ncols + namec]);
sprintf(buf, "%d", seq++);
s->rows[offs + 4] = xstrdup(buf);
offs += s->ncols;
}
}
}
if (rowp2) {
for (i = 1; i <= nrows2; i++) {
int nnrows, nncols;
char **rowpp;
if (*rowp2[i * ncols2 + namec2] != '(' ||
!strstr(rowp2[i * ncols2 + namec2], " autoindex ")) {
continue;
}
if (*rowp2[i * ncols2 + uniquec2] != '0') {
int k;
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA index_info('%q')",
&rowpp,
&nnrows, &nncols, NULL,
rowp2[i * ncols2 + namec2]);
if (ret != SQLITE_OK) {
continue;
}
for (k = 0; nnrows && k < nncols; k++) {
if (strcmp(rowpp[k], "name") == 0) {
int m;
for (m = 1; m <= nnrows; m++) {
int roffs = offs + (m - 1) * s->ncols;
s->rows[roffs + 0] = xstrdup("");
#if defined(_WIN32) || defined(_WIN64)
s->rows[roffs + 1] =
xstrdup(d->xcelqrx ? "main" : "");
#else
s->rows[roffs + 1] = xstrdup("");
#endif
s->rows[roffs + 2] = xstrdup(tname);
s->rows[roffs + 3] =
xstrdup(rowpp[m * nncols + k]);
s->rows[roffs + 5] =
xstrdup(rowp2[i * ncols2 + namec2]);
}
} else if (strcmp(rowpp[k], "seqno") == 0) {
int m;
for (m = 1; m <= nnrows; m++) {
int roffs = offs + (m - 1) * s->ncols;
int pos = m - 1;
char buf[32];
sscanf(rowpp[m * nncols + k], "%d", &pos);
sprintf(buf, "%d", pos + 1);
s->rows[roffs + 4] = xstrdup(buf);
}
}
}
offs += nnrows * s->ncols;
sqlite_free_table(rowpp);
}
}
}
sqlite_free_table(rowp);
sqlite_free_table(rowp2);
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Retrieve information about indexed columns.
* @param stmt statement handle
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLPrimaryKeys(SQLHSTMT stmt,
SQLCHAR *cat, SQLSMALLINT catLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *table, SQLSMALLINT tableLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvprimarykeys(stmt, cat, catLen, schema, schemaLen,
table, tableLen);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Retrieve information about indexed columns (UNICODE version).
* @param stmt statement handle
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLPrimaryKeysW(SQLHSTMT stmt,
SQLWCHAR *cat, SQLSMALLINT catLen,
SQLWCHAR *schema, SQLSMALLINT schemaLen,
SQLWCHAR *table, SQLSMALLINT tableLen)
{
char *c = NULL, *s = NULL, *t = NULL;
SQLRETURN ret;
HSTMT_LOCK(stmt);
if (cat) {
c = uc_to_utf_c(cat, catLen);
if (!c) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (schema) {
s = uc_to_utf_c(schema, schemaLen);
if (!s) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (table) {
t = uc_to_utf_c(table, tableLen);
if (!t) {
ret = nomem((STMT *) stmt);
goto done;
}
}
ret = drvprimarykeys(stmt, (SQLCHAR *) c, SQL_NTS,
(SQLCHAR *) s, SQL_NTS, (SQLCHAR *) t, SQL_NTS);
done:
HSTMT_UNLOCK(stmt);
uc_free(t);
uc_free(s);
uc_free(c);
return ret;
}
#endif
/**
* Columns for result set of SQLSpecialColumns().
*/
static COL scolSpec2[] = {
{ "SYSTEM", "COLUMN", "SCOPE", SQL_SMALLINT, 1 },
{ "SYSTEM", "COLUMN", "COLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "COLUMN", "DATA_TYPE", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "TYPE_NAME", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "PRECISION", SQL_INTEGER, 50 },
{ "SYSTEM", "COLUMN", "LENGTH", SQL_INTEGER, 50 },
{ "SYSTEM", "COLUMN", "DECIMAL_DIGITS", SQL_INTEGER, 50 },
{ "SYSTEM", "COLUMN", "PSEUDO_COLUMN", SQL_SMALLINT, 1 },
{ "SYSTEM", "COLUMN", "NULLABLE", SQL_SMALLINT, 1 }
};
static COL scolSpec3[] = {
{ "SYSTEM", "COLUMN", "SCOPE", SQL_SMALLINT, 1 },
{ "SYSTEM", "COLUMN", "COLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "COLUMN", "DATA_TYPE", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "TYPE_NAME", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "COLUMN_SIZE", SQL_INTEGER, 50 },
{ "SYSTEM", "COLUMN", "BUFFER_LENGTH", SQL_INTEGER, 50 },
{ "SYSTEM", "COLUMN", "DECIMAL_DIGITS", SQL_INTEGER, 50 },
{ "SYSTEM", "COLUMN", "PSEUDO_COLUMN", SQL_SMALLINT, 1 },
{ "SYSTEM", "COLUMN", "NULLABLE", SQL_SMALLINT, 1 }
};
/**
* Internal retrieve information about indexed columns.
* @param stmt statement handle
* @param id type of information, e.g. best row id
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param scope
* @param nullable
* @result ODBC error code
*/
static SQLRETURN
drvspecialcolumns(SQLHSTMT stmt, SQLUSMALLINT id,
SQLCHAR *cat, SQLSMALLINT catLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *table, SQLSMALLINT tableLen,
SQLUSMALLINT scope, SQLUSMALLINT nullable)
{
STMT *s;
DBC *d;
SQLRETURN sret;
int i, asize, ret, nrows, ncols, nnnrows, nnncols, offs;
int namec = -1, uniquec = -1, namecc = -1, typecc = -1;
int notnullcc = -1, mkrowid = 0;
PTRDIFF_T size;
char *errp = NULL, tname[512];
char **rowp = NULL, **rowppp = NULL;
sret = mkresultset(stmt, scolSpec2, array_size(scolSpec2),
scolSpec3, array_size(scolSpec3), &asize);
if (sret != SQL_SUCCESS) {
return sret;
}
s = (STMT *) stmt;
d = (DBC *) s->dbc;
if (!table || table[0] == '\0' || table[0] == '%') {
setstat(s, -1, "need table name", (*s->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
if (tableLen == SQL_NTS) {
size = sizeof (tname) - 1;
} else {
size = min(sizeof (tname) - 1, tableLen);
}
strncpy(tname, (char *) table, size);
tname[size] = '\0';
unescpat(tname);
if (id != SQL_BEST_ROWID) {
return SQL_SUCCESS;
}
sret = starttran(s);
if (sret != SQL_SUCCESS) {
return sret;
}
ret = sqlite_get_table_printf(d->sqlite, "PRAGMA index_list('%q')",
&rowp, &nrows, &ncols, &errp, tname);
if (ret != SQLITE_OK) {
doerr:
setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", ret);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_ERROR;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
size = 0; /* number result rows */
if (ncols * nrows <= 0) {
goto nodata_but_rowid;
}
ret = sqlite_get_table_printf(d->sqlite, "PRAGMA table_info('%q')",
&rowppp, &nnnrows, &nnncols, &errp, tname);
if (ret != SQLITE_OK) {
sqlite_free_table(rowp);
goto doerr;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
namec = findcol(rowp, ncols, "name");
uniquec = findcol(rowp, ncols, "unique");
if (namec < 0 || uniquec < 0) {
goto nodata_but_rowid;
}
namecc = findcol(rowppp, nnncols, "name");
typecc = findcol(rowppp, nnncols, "type");
notnullcc = findcol(rowppp, nnncols, "notnull");
for (i = 1; i <= nrows; i++) {
int nnrows, nncols;
char **rowpp;
if (*rowp[i * ncols + uniquec] != '0') {
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA index_info('%q')", &rowpp,
&nnrows, &nncols, NULL,
rowp[i * ncols + namec]);
if (ret == SQLITE_OK) {
size += nnrows;
sqlite_free_table(rowpp);
}
}
}
nodata_but_rowid:
if (size == 0) {
size = 1;
mkrowid = 1;
}
s->nrows = size;
size = (size + 1) * asize;
s->rows = xmalloc((size + 1) * sizeof (char *));
if (!s->rows) {
s->nrows = 0;
sqlite_free_table(rowp);
sqlite_free_table(rowppp);
return nomem(s);
}
s->rows[0] = (char *) size;
s->rows += 1;
memset(s->rows, 0, sizeof (char *) * size);
s->rowfree = freerows;
if (mkrowid) {
s->nrows = 0;
goto mkrowid;
}
offs = 0;
for (i = 1; i <= nrows; i++) {
int nnrows, nncols;
char **rowpp;
if (*rowp[i * ncols + uniquec] != '0') {
int k;
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA index_info('%q')", &rowpp,
&nnrows, &nncols, NULL,
rowp[i * ncols + namec]);
if (ret != SQLITE_OK) {
continue;
}
for (k = 0; nnrows && k < nncols; k++) {
if (strcmp(rowpp[k], "name") == 0) {
int m;
for (m = 1; m <= nnrows; m++) {
int roffs = (offs + m) * s->ncols;
s->rows[roffs + 0] =
xstrdup(stringify(SQL_SCOPE_SESSION));
s->rows[roffs + 1] = xstrdup(rowpp[m * nncols + k]);
s->rows[roffs + 4] = xstrdup("0");
s->rows[roffs + 7] =
xstrdup(stringify(SQL_PC_NOT_PSEUDO));
if (namecc >= 0 && typecc >= 0) {
int ii;
for (ii = 1; ii <= nnnrows; ii++) {
if (strcmp(rowppp[ii * nnncols + namecc],
rowpp[m * nncols + k]) == 0) {
char *typen = rowppp[ii * nnncols + typecc];
int sqltype, mm, dd, isnullable = 0;
char buf[32];
s->rows[roffs + 3] = xstrdup(typen);
sqltype = mapsqltype(typen, NULL, *s->ov3,
s->nowchar[0]);
getmd(typen, sqltype, &mm, &dd);
#ifdef SQL_LONGVARCHAR
if (sqltype == SQL_VARCHAR && mm > 255) {
sqltype = SQL_LONGVARCHAR;
}
#endif
#ifdef WINTERFACE
#ifdef SQL_WLONGVARCHAR
if (sqltype == SQL_WVARCHAR && mm > 255) {
sqltype = SQL_WLONGVARCHAR;
}
#endif
#endif
#if HAVE_ENCDEC
if (sqltype == SQL_VARBINARY && mm > 255) {
sqltype = SQL_LONGVARBINARY;
}
#endif
sprintf(buf, "%d", sqltype);
s->rows[roffs + 2] = xstrdup(buf);
sprintf(buf, "%d", mm);
s->rows[roffs + 5] = xstrdup(buf);
sprintf(buf, "%d", dd);
s->rows[roffs + 6] = xstrdup(buf);
if (notnullcc >= 0) {
char *inp =
rowppp[ii * nnncols + notnullcc];
isnullable = inp[0] != '0';
}
sprintf(buf, "%d", isnullable);
s->rows[roffs + 8] = xstrdup(buf);
}
}
}
}
}
}
offs += nnrows;
sqlite_free_table(rowpp);
}
}
if (nullable == SQL_NO_NULLS) {
for (i = 1; i < s->nrows; i++) {
if (s->rows[i * s->ncols + 8][0] == '0') {
int m, i1 = i + 1;
for (m = 0; m < s->ncols; m++) {
freep(&s->rows[i * s->ncols + m]);
}
size = s->ncols * sizeof (char *) * (s->nrows - i1);
if (size > 0) {
memmove(s->rows + i * s->ncols,
s->rows + i1 * s->ncols,
size);
memset(s->rows + s->nrows * s->ncols, 0,
s->ncols * sizeof (char *));
}
s->nrows--;
--i;
}
}
}
mkrowid:
sqlite_free_table(rowp);
sqlite_free_table(rowppp);
if (s->nrows == 0) {
s->rows[s->ncols + 0] = xstrdup(stringify(SQL_SCOPE_SESSION));
s->rows[s->ncols + 1] = xstrdup("_ROWID_");
s->rows[s->ncols + 2] = xstrdup(stringify(SQL_INTEGER));
s->rows[s->ncols + 3] = xstrdup("integer");
s->rows[s->ncols + 4] = xstrdup("0");
s->rows[s->ncols + 5] = xstrdup("10");
s->rows[s->ncols + 6] = xstrdup("9");
s->rows[s->ncols + 7] = xstrdup(stringify(SQL_PC_PSEUDO));
s->rows[s->ncols + 8] = xstrdup(stringify(SQL_FALSE));
s->nrows = 1;
}
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Retrieve information about indexed columns.
* @param stmt statement handle
* @param id type of information, e.g. best row id
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param scope
* @param nullable
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSpecialColumns(SQLHSTMT stmt, SQLUSMALLINT id,
SQLCHAR *cat, SQLSMALLINT catLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *table, SQLSMALLINT tableLen,
SQLUSMALLINT scope, SQLUSMALLINT nullable)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvspecialcolumns(stmt, id, cat, catLen, schema, schemaLen,
table, tableLen, scope, nullable);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Retrieve information about indexed columns (UNICODE version).
* @param stmt statement handle
* @param id type of information, e.g. best row id
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param scope
* @param nullable
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSpecialColumnsW(SQLHSTMT stmt, SQLUSMALLINT id,
SQLWCHAR *cat, SQLSMALLINT catLen,
SQLWCHAR *schema, SQLSMALLINT schemaLen,
SQLWCHAR *table, SQLSMALLINT tableLen,
SQLUSMALLINT scope, SQLUSMALLINT nullable)
{
char *c = NULL, *s = NULL, *t = NULL;
SQLRETURN ret;
HSTMT_LOCK(stmt);
if (cat) {
c = uc_to_utf_c(cat, catLen);
if (!c) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (schema) {
s = uc_to_utf_c(schema, schemaLen);
if (!s) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (table) {
t = uc_to_utf_c(table, tableLen);
if (!t) {
ret = nomem((STMT *) stmt);
goto done;
}
}
ret = drvspecialcolumns(stmt, id, (SQLCHAR *) c, SQL_NTS,
(SQLCHAR *) s, SQL_NTS, (SQLCHAR *) t, SQL_NTS,
scope, nullable);
done:
HSTMT_UNLOCK(stmt);
uc_free(t);
uc_free(s);
uc_free(c);
return ret;
}
#endif
/**
* Columns for result set of SQLForeignKeys().
*/
static COL fkeySpec2[] = {
{ "SYSTEM", "FOREIGNKEY", "PKTABLE_QUALIFIER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "FOREIGNKEY", "PKTABLE_OWNER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "FOREIGNKEY", "PKTABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "FOREIGNKEY", "PKCOLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "FOREIGNKEY", "FKTABLE_QUALIFIER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "FOREIGNKEY", "FKTABLE_OWNER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "FOREIGNKEY", "FKTABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "FOREIGNKEY", "FKCOLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "FOREIGNKEY", "KEY_SEQ", SQL_SMALLINT, 5 },
{ "SYSTEM", "FOREIGNKEY", "UPDATE_RULE", SQL_SMALLINT, 5 },
{ "SYSTEM", "FOREIGNKEY", "DELETE_RULE", SQL_SMALLINT, 5 },
{ "SYSTEM", "FOREIGNKEY", "FK_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "FOREIGNKEY", "PK_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "FOREIGNKEY", "DEFERRABILITY", SQL_SMALLINT, 5 }
};
static COL fkeySpec3[] = {
{ "SYSTEM", "FOREIGNKEY", "PKTABLE_CAT", SCOL_VARCHAR, 50 },
{ "SYSTEM", "FOREIGNKEY", "PKTABLE_SCHEM", SCOL_VARCHAR, 50 },
{ "SYSTEM", "FOREIGNKEY", "PKTABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "FOREIGNKEY", "PKCOLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "FOREIGNKEY", "FKTABLE_CAT", SCOL_VARCHAR, 50 },
{ "SYSTEM", "FOREIGNKEY", "FKTABLE_SCHEM", SCOL_VARCHAR, 50 },
{ "SYSTEM", "FOREIGNKEY", "FKTABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "FOREIGNKEY", "FKCOLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "FOREIGNKEY", "KEY_SEQ", SQL_SMALLINT, 5 },
{ "SYSTEM", "FOREIGNKEY", "UPDATE_RULE", SQL_SMALLINT, 5 },
{ "SYSTEM", "FOREIGNKEY", "DELETE_RULE", SQL_SMALLINT, 5 },
{ "SYSTEM", "FOREIGNKEY", "FK_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "FOREIGNKEY", "PK_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "FOREIGNKEY", "DEFERRABILITY", SQL_SMALLINT, 5 }
};
/**
* Internal retrieve information about primary/foreign keys.
* @param stmt statement handle
* @param PKcatalog primary key catalog name/pattern or NULL
* @param PKcatalogLen length of PKcatalog or SQL_NTS
* @param PKschema primary key schema name/pattern or NULL
* @param PKschemaLen length of PKschema or SQL_NTS
* @param PKtable primary key table name/pattern or NULL
* @param PKtableLen length of PKtable or SQL_NTS
* @param FKcatalog foreign key catalog name/pattern or NULL
* @param FKcatalogLen length of FKcatalog or SQL_NTS
* @param FKschema foreign key schema name/pattern or NULL
* @param FKschemaLen length of FKschema or SQL_NTS
* @param FKtable foreign key table name/pattern or NULL
* @param FKtableLen length of FKtable or SQL_NTS
* @result ODBC error code
*/
static SQLRETURN SQL_API
drvforeignkeys(SQLHSTMT stmt,
SQLCHAR *PKcatalog, SQLSMALLINT PKcatalogLen,
SQLCHAR *PKschema, SQLSMALLINT PKschemaLen,
SQLCHAR *PKtable, SQLSMALLINT PKtableLen,
SQLCHAR *FKcatalog, SQLSMALLINT FKcatalogLen,
SQLCHAR *FKschema, SQLSMALLINT FKschemaLen,
SQLCHAR *FKtable, SQLSMALLINT FKtableLen)
{
STMT *s;
DBC *d;
SQLRETURN sret;
int i, asize, ret, nrows, ncols, offs, namec, seqc, fromc, toc;
PTRDIFF_T size;
char **rowp, *errp = NULL, pname[512], fname[512];
sret = mkresultset(stmt, fkeySpec2, array_size(fkeySpec2),
fkeySpec3, array_size(fkeySpec3), &asize);
if (sret != SQL_SUCCESS) {
return sret;
}
s = (STMT *) stmt;
sret = starttran(s);
if (sret != SQL_SUCCESS) {
return sret;
}
d = (DBC *) s->dbc;
if ((!PKtable || PKtable[0] == '\0' || PKtable[0] == '%') &&
(!FKtable || FKtable[0] == '\0' || FKtable[0] == '%')) {
setstat(s, -1, "need table name", (*s->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
size = 0;
if (PKtable) {
if (PKtableLen == SQL_NTS) {
size = sizeof (pname) - 1;
} else {
size = min(sizeof (pname) - 1, PKtableLen);
}
strncpy(pname, (char *) PKtable, size);
}
pname[size] = '\0';
size = 0;
if (FKtable) {
if (FKtableLen == SQL_NTS) {
size = sizeof (fname) - 1;
} else {
size = min(sizeof (fname) - 1, FKtableLen);
}
strncpy(fname, (char *) FKtable, size);
}
fname[size] = '\0';
if (fname[0] != '\0') {
int plen;
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA foreign_key_list('%q')", &rowp,
&nrows, &ncols, &errp, fname);
if (ret != SQLITE_OK) {
setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", ret);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_ERROR;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
if (ncols * nrows <= 0) {
nodata:
sqlite_free_table(rowp);
return SQL_SUCCESS;
}
size = 0;
namec = findcol(rowp, ncols, "table");
seqc = findcol(rowp, ncols, "seq");
fromc = findcol(rowp, ncols, "from");
toc = findcol(rowp, ncols, "to");
if (namec < 0 || seqc < 0 || fromc < 0 || toc < 0) {
goto nodata;
}
plen = strlen(pname);
for (i = 1; i <= nrows; i++) {
char *ptab = unquote(rowp[i * ncols + namec]);
if (plen && ptab) {
int len = strlen(ptab);
if (plen != len || strncasecmp(pname, ptab, plen) != 0) {
continue;
}
}
size++;
}
if (size == 0) {
goto nodata;
}
s->nrows = size;
size = (size + 1) * asize;
s->rows = xmalloc((size + 1) * sizeof (char *));
if (!s->rows) {
s->nrows = 0;
return nomem(s);
}
s->rows[0] = (char *) size;
s->rows += 1;
memset(s->rows, 0, sizeof (char *) * size);
s->rowfree = freerows;
offs = 0;
for (i = 1; i <= nrows; i++) {
int pos = 0, roffs = (offs + 1) * s->ncols;
char *ptab = rowp[i * ncols + namec];
char buf[32];
if (plen && ptab) {
int len = strlen(ptab);
if (plen != len || strncasecmp(pname, ptab, plen) != 0) {
continue;
}
}
s->rows[roffs + 0] = xstrdup("");
#if defined(_WIN32) || defined(_WIN64)
s->rows[roffs + 1] = xstrdup(d->xcelqrx ? "main" : "");
#else
s->rows[roffs + 1] = xstrdup("");
#endif
s->rows[roffs + 2] = xstrdup(ptab);
s->rows[roffs + 3] = xstrdup(rowp[i * ncols + toc]);
s->rows[roffs + 4] = xstrdup("");
s->rows[roffs + 5] = xstrdup("");
s->rows[roffs + 6] = xstrdup(fname);
s->rows[roffs + 7] = xstrdup(rowp[i * ncols + fromc]);
sscanf(rowp[i * ncols + seqc], "%d", &pos);
sprintf(buf, "%d", pos + 1);
s->rows[roffs + 8] = xstrdup(buf);
s->rows[roffs + 9] = xstrdup(stringify(SQL_NO_ACTION));
s->rows[roffs + 10] = xstrdup(stringify(SQL_NO_ACTION));
s->rows[roffs + 11] = NULL;
s->rows[roffs + 12] = NULL;
s->rows[roffs + 13] = xstrdup(stringify(SQL_NOT_DEFERRABLE));
offs++;
}
sqlite_free_table(rowp);
} else {
int nnrows, nncols, plen = strlen(pname);
char **rowpp;
ret = sqlite_get_table(d->sqlite,
"select name from sqlite_master "
"where type='table'", &rowp,
&nrows, &ncols, &errp);
if (ret != SQLITE_OK) {
setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", ret);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_ERROR;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
if (ncols * nrows <= 0) {
goto nodata;
}
size = 0;
for (i = 1; i <= nrows; i++) {
int k;
if (!rowp[i]) {
continue;
}
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA foreign_key_list('%q')",
&rowpp,
&nnrows, &nncols, NULL, rowp[i]);
if (ret != SQLITE_OK || nncols * nnrows <= 0) {
sqlite_free_table(rowpp);
continue;
}
namec = findcol(rowpp, nncols, "table");
seqc = findcol(rowpp, nncols, "seq");
fromc = findcol(rowpp, nncols, "from");
toc = findcol(rowpp, nncols, "to");
if (namec < 0 || seqc < 0 || fromc < 0 || toc < 0) {
sqlite_free_table(rowpp);
continue;
}
for (k = 1; k <= nnrows; k++) {
char *ptab = unquote(rowpp[k * nncols + namec]);
if (plen && ptab) {
int len = strlen(ptab);
if (len != plen || strncasecmp(pname, ptab, plen) != 0) {
continue;
}
}
size++;
}
sqlite_free_table(rowpp);
}
if (size == 0) {
goto nodata;
}
s->nrows = size;
size = (size + 1) * asize;
s->rows = xmalloc((size + 1) * sizeof (char *));
if (!s->rows) {
s->nrows = 0;
return nomem(s);
}
s->rows[0] = (char *) size;
s->rows += 1;
memset(s->rows, 0, sizeof (char *) * size);
s->rowfree = freerows;
offs = 0;
for (i = 1; i <= nrows; i++) {
int k;
if (!rowp[i]) {
continue;
}
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA foreign_key_list('%q')",
&rowpp,
&nnrows, &nncols, NULL, rowp[i]);
if (ret != SQLITE_OK || nncols * nnrows <= 0) {
sqlite_free_table(rowpp);
continue;
}
namec = findcol(rowpp, nncols, "table");
seqc = findcol(rowpp, nncols, "seq");
fromc = findcol(rowpp, nncols, "from");
toc = findcol(rowpp, nncols, "to");
if (namec < 0 || seqc < 0 || fromc < 0 || toc < 0) {
sqlite_free_table(rowpp);
continue;
}
for (k = 1; k <= nnrows; k++) {
int pos = 0, roffs = (offs + 1) * s->ncols;
char *ptab = unquote(rowpp[k * nncols + namec]);
char buf[32];
if (plen && ptab) {
int len = strlen(ptab);
if (len != plen || strncasecmp(pname, ptab, plen) != 0) {
continue;
}
}
s->rows[roffs + 0] = xstrdup("");
#if defined(_WIN32) || defined(_WIN64)
s->rows[roffs + 1] = xstrdup(d->xcelqrx ? "main" : "");
#else
s->rows[roffs + 1] = xstrdup("");
#endif
s->rows[roffs + 2] = xstrdup(ptab);
s->rows[roffs + 3] = xstrdup(rowpp[k * nncols + toc]);
s->rows[roffs + 4] = xstrdup("");
s->rows[roffs + 5] = xstrdup("");
s->rows[roffs + 6] = xstrdup(rowp[i]);
s->rows[roffs + 7] = xstrdup(rowpp[k * nncols + fromc]);
sscanf(rowpp[k * nncols + seqc], "%d", &pos);
sprintf(buf, "%d", pos + 1);
s->rows[roffs + 8] = xstrdup(buf);
s->rows[roffs + 9] = xstrdup(stringify(SQL_NO_ACTION));
s->rows[roffs + 10] = xstrdup(stringify(SQL_NO_ACTION));
s->rows[roffs + 11] = NULL;
s->rows[roffs + 12] = NULL;
s->rows[roffs + 13] = xstrdup(stringify(SQL_NOT_DEFERRABLE));
offs++;
}
sqlite_free_table(rowpp);
}
sqlite_free_table(rowp);
}
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Retrieve information about primary/foreign keys.
* @param stmt statement handle
* @param PKcatalog primary key catalog name/pattern or NULL
* @param PKcatalogLen length of PKcatalog or SQL_NTS
* @param PKschema primary key schema name/pattern or NULL
* @param PKschemaLen length of PKschema or SQL_NTS
* @param PKtable primary key table name/pattern or NULL
* @param PKtableLen length of PKtable or SQL_NTS
* @param FKcatalog foreign key catalog name/pattern or NULL
* @param FKcatalogLen length of FKcatalog or SQL_NTS
* @param FKschema foreign key schema name/pattern or NULL
* @param FKschemaLen length of FKschema or SQL_NTS
* @param FKtable foreign key table name/pattern or NULL
* @param FKtableLen length of FKtable or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLForeignKeys(SQLHSTMT stmt,
SQLCHAR *PKcatalog, SQLSMALLINT PKcatalogLen,
SQLCHAR *PKschema, SQLSMALLINT PKschemaLen,
SQLCHAR *PKtable, SQLSMALLINT PKtableLen,
SQLCHAR *FKcatalog, SQLSMALLINT FKcatalogLen,
SQLCHAR *FKschema, SQLSMALLINT FKschemaLen,
SQLCHAR *FKtable, SQLSMALLINT FKtableLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvforeignkeys(stmt,
PKcatalog, PKcatalogLen,
PKschema, PKschemaLen, PKtable, PKtableLen,
FKcatalog, FKcatalogLen,
FKschema, FKschemaLen,
FKtable, FKtableLen);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Retrieve information about primary/foreign keys (UNICODE version).
* @param stmt statement handle
* @param PKcatalog primary key catalog name/pattern or NULL
* @param PKcatalogLen length of PKcatalog or SQL_NTS
* @param PKschema primary key schema name/pattern or NULL
* @param PKschemaLen length of PKschema or SQL_NTS
* @param PKtable primary key table name/pattern or NULL
* @param PKtableLen length of PKtable or SQL_NTS
* @param FKcatalog foreign key catalog name/pattern or NULL
* @param FKcatalogLen length of FKcatalog or SQL_NTS
* @param FKschema foreign key schema name/pattern or NULL
* @param FKschemaLen length of FKschema or SQL_NTS
* @param FKtable foreign key table name/pattern or NULL
* @param FKtableLen length of FKtable or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLForeignKeysW(SQLHSTMT stmt,
SQLWCHAR *PKcatalog, SQLSMALLINT PKcatalogLen,
SQLWCHAR *PKschema, SQLSMALLINT PKschemaLen,
SQLWCHAR *PKtable, SQLSMALLINT PKtableLen,
SQLWCHAR *FKcatalog, SQLSMALLINT FKcatalogLen,
SQLWCHAR *FKschema, SQLSMALLINT FKschemaLen,
SQLWCHAR *FKtable, SQLSMALLINT FKtableLen)
{
char *pc = NULL, *ps = NULL, *pt = NULL;
char *fc = NULL, *fs = NULL, *ft = NULL;
SQLRETURN ret;
HSTMT_LOCK(stmt);
if (PKcatalog) {
pc = uc_to_utf_c(PKcatalog, PKcatalogLen);
if (!pc) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (PKschema) {
ps = uc_to_utf_c(PKschema, PKschemaLen);
if (!ps) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (PKtable) {
pt = uc_to_utf_c(PKtable, PKtableLen);
if (!pt) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (FKcatalog) {
fc = uc_to_utf_c(FKcatalog, FKcatalogLen);
if (!fc) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (FKschema) {
fs = uc_to_utf_c(FKschema, FKschemaLen);
if (!fs) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (FKtable) {
ft = uc_to_utf_c(FKtable, FKtableLen);
if (!ft) {
ret = nomem((STMT *) stmt);
goto done;
}
}
ret = drvforeignkeys(stmt, (SQLCHAR *) pc, SQL_NTS,
(SQLCHAR *) ps, SQL_NTS, (SQLCHAR *) pt, SQL_NTS,
(SQLCHAR *) fc, SQL_NTS, (SQLCHAR *) fs, SQL_NTS,
(SQLCHAR *) ft, SQL_NTS);
done:
HSTMT_UNLOCK(stmt);
uc_free(ft);
uc_free(fs);
uc_free(fc);
uc_free(pt);
uc_free(ps);
uc_free(pc);
return ret;
}
#endif
/**
* Start transaction when autocommit off
* @param s statement pointer
* @result ODBC error code
*/
static SQLRETURN
starttran(STMT *s)
{
int ret = SQL_SUCCESS, rc;
char *errp = NULL;
DBC *d = (DBC *) s->dbc;
if (!d->autocommit && !d->intrans && !d->trans_disable) {
rc = sqlite_exec(d->sqlite, "BEGIN TRANSACTION", NULL, NULL, &errp);
dbtracerc(d, rc, errp);
if (rc != SQLITE_OK) {
setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", rc);
ret = SQL_ERROR;
} else {
d->intrans = 1;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
}
return ret;
}
/**
* Internal commit or rollback transaction.
* @param d database connection pointer
* @param comptype type of transaction's end, SQL_COMMIT or SQL_ROLLBACK
* @param force force action regardless of DBC's autocommit state
* @result ODBC error code
*/
static SQLRETURN
endtran(DBC *d, SQLSMALLINT comptype, int force)
{
int fail = 0, ret;
char *sql, *errp = NULL;
if (!d->sqlite) {
setstatd(d, -1, "not connected", (*d->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
if ((!force && d->autocommit) || !d->intrans) {
return SQL_SUCCESS;
}
switch (comptype) {
case SQL_COMMIT:
sql = "COMMIT TRANSACTION";
goto doit;
case SQL_ROLLBACK:
rollback:
sql = "ROLLBACK TRANSACTION";
doit:
d->intrans = 0;
ret = sqlite_exec(d->sqlite, sql, NULL, NULL, &errp);
dbtracerc(d, ret, errp);
if (ret != SQLITE_OK) {
if (!fail) {
setstatd(d, ret, "%s", (*d->ov3) ? "HY000" : "S1000",
errp ? errp : "transaction failed");
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
fail = 1;
goto rollback;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_ERROR;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_SUCCESS;
}
setstatd(d, -1, "invalid completion type", (*d->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
/**
* Internal commit or rollback transaction.
* @param type type of handle
* @param handle HDBC, HENV, or HSTMT handle
* @param comptype SQL_COMMIT or SQL_ROLLBACK
* @result ODBC error code
*/
static SQLRETURN
drvendtran(SQLSMALLINT type, SQLHANDLE handle, SQLSMALLINT comptype)
{
DBC *d;
int fail = 0;
SQLRETURN ret;
#if defined(_WIN32) || defined(_WIN64)
ENV *e;
#endif
switch (type) {
case SQL_HANDLE_DBC:
HDBC_LOCK((SQLHDBC) handle);
if (handle == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
d = (DBC *) handle;
ret = endtran(d, comptype, 0);
HDBC_UNLOCK((SQLHDBC) handle);
return ret;
case SQL_HANDLE_ENV:
if (handle == SQL_NULL_HENV) {
return SQL_INVALID_HANDLE;
}
#if defined(_WIN32) || defined(_WIN64)
e = (ENV *) handle;
if (e->magic != ENV_MAGIC) {
return SQL_INVALID_HANDLE;
}
EnterCriticalSection(&e->cs);
e->owner = GetCurrentThreadId();
#endif
d = ((ENV *) handle)->dbcs;
while (d) {
ret = endtran(d, comptype, 0);
if (ret != SQL_SUCCESS) {
fail++;
comptype = SQL_ROLLBACK;
}
d = d->next;
}
#if defined(_WIN32) || defined(_WIN64)
LeaveCriticalSection(&e->cs);
e->owner = 0;
#endif
return fail ? SQL_ERROR : SQL_SUCCESS;
}
return SQL_INVALID_HANDLE;
}
/**
* Commit or rollback transaction.
* @param type type of handle
* @param handle HDBC, HENV, or HSTMT handle
* @param comptype SQL_COMMIT or SQL_ROLLBACK
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLEndTran(SQLSMALLINT type, SQLHANDLE handle, SQLSMALLINT comptype)
{
return drvendtran(type, handle, comptype);
}
/**
* Commit or rollback transaction.
* @param env environment handle or NULL
* @param dbc database connection handle or NULL
* @param type SQL_COMMIT or SQL_ROLLBACK
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLTransact(SQLHENV env, SQLHDBC dbc, SQLUSMALLINT type)
{
if (env != SQL_NULL_HENV) {
return drvendtran(SQL_HANDLE_ENV, (SQLHANDLE) env, type);
}
return drvendtran(SQL_HANDLE_DBC, (SQLHANDLE) dbc, type);
}
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLCopyDesc(SQLHDESC source, SQLHDESC target)
{
return SQL_ERROR;
}
#ifndef WINTERFACE
/**
* Translate SQL string.
* @param stmt statement handle
* @param sqlin input string
* @param sqlinLen length of input string
* @param sql output string
* @param sqlMax max space in output string
* @param sqlLen value return for length of output string
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLNativeSql(SQLHSTMT stmt, SQLCHAR *sqlin, SQLINTEGER sqlinLen,
SQLCHAR *sql, SQLINTEGER sqlMax, SQLINTEGER *sqlLen)
{
int outLen = 0;
SQLRETURN ret = SQL_SUCCESS;
HSTMT_LOCK(stmt);
if (sqlinLen == SQL_NTS) {
sqlinLen = strlen((char *) sqlin);
}
if (sql) {
if (sqlMax > 0) {
strncpy((char *) sql, (char *) sqlin, sqlMax - 1);
sqlin[sqlMax - 1] = '\0';
outLen = min(sqlMax - 1, sqlinLen);
}
} else {
outLen = sqlinLen;
}
if (sqlLen) {
*sqlLen = outLen;
}
if (sql && outLen < sqlinLen) {
setstat((STMT *) stmt, -1, "data right truncated", "01004");
ret = SQL_SUCCESS_WITH_INFO;
}
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Translate SQL string (UNICODE version).
* @param stmt statement handle
* @param sqlin input string
* @param sqlinLen length of input string
* @param sql output string
* @param sqlMax max space in output string
* @param sqlLen value return for length of output string
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLNativeSqlW(SQLHSTMT stmt, SQLWCHAR *sqlin, SQLINTEGER sqlinLen,
SQLWCHAR *sql, SQLINTEGER sqlMax, SQLINTEGER *sqlLen)
{
int outLen = 0;
SQLRETURN ret = SQL_SUCCESS;
HSTMT_LOCK(stmt);
if (sqlinLen == SQL_NTS) {
sqlinLen = uc_strlen(sqlin);
}
if (sql) {
if (sqlMax > 0) {
uc_strncpy(sql, sqlin, sqlMax - 1);
sqlin[sqlMax - 1] = 0;
outLen = min(sqlMax - 1, sqlinLen);
}
} else {
outLen = sqlinLen;
}
if (sqlLen) {
*sqlLen = outLen;
}
if (sql && outLen < sqlinLen) {
setstat((STMT *) stmt, -1, "data right truncated", "01004");
ret = SQL_SUCCESS_WITH_INFO;
}
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
/**
* Columns for result set of SQLProcedures().
*/
static COL procSpec2[] = {
{ "SYSTEM", "PROCEDURE", "PROCEDURE_QUALIFIER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCEDURE", "PROCEDURE_OWNER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCEDURE", "PROCEDURE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "PROCEDURE", "NUM_INPUT_PARAMS", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCEDURE", "NUM_OUTPUT_PARAMS", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCEDURE", "NUM_RESULT_SETS", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCEDURE", "REMARKS", SCOL_VARCHAR, 255 },
{ "SYSTEM", "PROCEDURE", "PROCEDURE_TYPE", SQL_SMALLINT, 5 }
};
static COL procSpec3[] = {
{ "SYSTEM", "PROCEDURE", "PROCEDURE_CAT", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCEDURE", "PROCEDURE_SCHEM", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCEDURE", "PROCEDURE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "PROCEDURE", "NUM_INPUT_PARAMS", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCEDURE", "NUM_OUTPUT_PARAMS", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCEDURE", "NUM_RESULT_SETS", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCEDURE", "REMARKS", SCOL_VARCHAR, 255 },
{ "SYSTEM", "PROCEDURE", "PROCEDURE_TYPE", SQL_SMALLINT, 5 }
};
#ifndef WINTERFACE
/**
* Retrieve information about stored procedures.
* @param stmt statement handle
* @param catalog catalog name/pattern or NULL
* @param catalogLen length of catalog or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema or SQL_NTS
* @param proc procedure name/pattern or NULL
* @param procLen length of proc or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLProcedures(SQLHSTMT stmt,
SQLCHAR *catalog, SQLSMALLINT catalogLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *proc, SQLSMALLINT procLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = mkresultset(stmt, procSpec2, array_size(procSpec2),
procSpec3, array_size(procSpec3), NULL);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Retrieve information about stored procedures (UNICODE version).
* @param stmt statement handle
* @param catalog catalog name/pattern or NULL
* @param catalogLen length of catalog or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema or SQL_NTS
* @param proc procedure name/pattern or NULL
* @param procLen length of proc or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLProceduresW(SQLHSTMT stmt,
SQLWCHAR *catalog, SQLSMALLINT catalogLen,
SQLWCHAR *schema, SQLSMALLINT schemaLen,
SQLWCHAR *proc, SQLSMALLINT procLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = mkresultset(stmt, procSpec2, array_size(procSpec2),
procSpec3, array_size(procSpec3), NULL);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
/**
* Columns for result set of SQLProcedureColumns().
*/
static COL procColSpec2[] = {
{ "SYSTEM", "PROCCOL", "PROCEDURE_QUALIFIER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCCOL", "PROCEDURE_OWNER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCCOL", "PROCEDURE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "PROCCOL", "COLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "PROCCOL", "COLUMN_TYPE", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "DATA_TYPE", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "TYPE_NAME", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCCOL", "PRECISION", SQL_INTEGER, 10 },
{ "SYSTEM", "PROCCOL", "LENGTH", SQL_INTEGER, 10 },
{ "SYSTEM", "PROCCOL", "SCALE", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "RADIX", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "NULLABLE", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "REMARKS", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCCOL", "COLUMN_DEF", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCCOL", "SQL_DATA_TYPE", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "SQL_DATETIME_SUB", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "CHAR_OCTET_LENGTH", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "ORDINAL_POSITION", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "IS_NULLABLE", SCOL_VARCHAR, 50 }
};
static COL procColSpec3[] = {
{ "SYSTEM", "PROCCOL", "PROCEDURE_CAT", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCCOL", "PROCEDURE_SCHEM", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCCOL", "PROCEDURE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "PROCCOL", "COLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "PROCCOL", "COLUMN_TYPE", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "DATA_TYPE", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "TYPE_NAME", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCCOL", "COLUMN_SIZE", SQL_INTEGER, 10 },
{ "SYSTEM", "PROCCOL", "BUFFER_LENGTH", SQL_INTEGER, 10 },
{ "SYSTEM", "PROCCOL", "DECIMAL_DIGITS", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "NUM_PREC_RADIX", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "NULLABLE", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "REMARKS", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCCOL", "COLUMN_DEF", SCOL_VARCHAR, 50 },
{ "SYSTEM", "PROCCOL", "SQL_DATA_TYPE", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "SQL_DATETIME_SUB", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "CHAR_OCTET_LENGTH", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "ORDINAL_POSITION", SQL_SMALLINT, 5 },
{ "SYSTEM", "PROCCOL", "IS_NULLABLE", SCOL_VARCHAR, 50 }
};
#ifndef WINTERFACE
/**
* Retrieve information about columns in result set of stored procedures.
* @param stmt statement handle
* @param catalog catalog name/pattern or NULL
* @param catalogLen length of catalog or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema or SQL_NTS
* @param proc procedure name/pattern or NULL
* @param procLen length of proc or SQL_NTS
* @param column column name/pattern or NULL
* @param columnLen length of column or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLProcedureColumns(SQLHSTMT stmt,
SQLCHAR *catalog, SQLSMALLINT catalogLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *proc, SQLSMALLINT procLen,
SQLCHAR *column, SQLSMALLINT columnLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = mkresultset(stmt, procColSpec2, array_size(procColSpec2),
procColSpec3, array_size(procColSpec3), NULL);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Retrieve information about columns in result
* set of stored procedures (UNICODE version).
* @param stmt statement handle
* @param catalog catalog name/pattern or NULL
* @param catalogLen length of catalog or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema or SQL_NTS
* @param proc procedure name/pattern or NULL
* @param procLen length of proc or SQL_NTS
* @param column column name/pattern or NULL
* @param columnLen length of column or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLProcedureColumnsW(SQLHSTMT stmt,
SQLWCHAR *catalog, SQLSMALLINT catalogLen,
SQLWCHAR *schema, SQLSMALLINT schemaLen,
SQLWCHAR *proc, SQLSMALLINT procLen,
SQLWCHAR *column, SQLSMALLINT columnLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = mkresultset(stmt, procColSpec2, array_size(procColSpec2),
procColSpec3, array_size(procColSpec3), NULL);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
/**
* Get information of HENV.
* @param env environment handle
* @param attr attribute to be retrieved
* @param val output buffer
* @param len length of output buffer
* @param lenp output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER val,
SQLINTEGER len, SQLINTEGER *lenp)
{
ENV *e;
SQLRETURN ret = SQL_ERROR;
if (env == SQL_NULL_HENV) {
return SQL_INVALID_HANDLE;
}
e = (ENV *) env;
if (!e || e->magic != ENV_MAGIC) {
return SQL_INVALID_HANDLE;
}
#if defined(_WIN32) || defined(_WIN64)
EnterCriticalSection(&e->cs);
e->owner = GetCurrentThreadId();
#endif
switch (attr) {
case SQL_ATTR_CONNECTION_POOLING:
ret = SQL_ERROR;
break;
case SQL_ATTR_CP_MATCH:
ret = SQL_NO_DATA;
break;
case SQL_ATTR_OUTPUT_NTS:
if (val) {
*((SQLINTEGER *) val) = SQL_TRUE;
}
if (lenp) {
*lenp = sizeof (SQLINTEGER);
}
ret = SQL_SUCCESS;
break;
case SQL_ATTR_ODBC_VERSION:
if (val) {
*((SQLINTEGER *) val) = e->ov3 ? SQL_OV_ODBC3 : SQL_OV_ODBC2;
}
if (lenp) {
*lenp = sizeof (SQLINTEGER);
}
ret = SQL_SUCCESS;
break;
}
#if defined(_WIN32) || defined(_WIN64)
e->owner = 0;
LeaveCriticalSection(&e->cs);
#endif
return ret;
}
/**
* Set information in HENV.
* @param env environment handle
* @param attr attribute to be retrieved
* @param val parameter buffer
* @param len length of parameter
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSetEnvAttr(SQLHENV env, SQLINTEGER attr, SQLPOINTER val, SQLINTEGER len)
{
ENV *e;
SQLRETURN ret = SQL_ERROR;
if (env == SQL_NULL_HENV) {
return SQL_INVALID_HANDLE;
}
e = (ENV *) env;
if (!e || e->magic != ENV_MAGIC) {
return SQL_INVALID_HANDLE;
}
#if defined(_WIN32) || defined(_WIN64)
EnterCriticalSection(&e->cs);
e->owner = GetCurrentThreadId();
#endif
switch (attr) {
case SQL_ATTR_CONNECTION_POOLING:
ret = SQL_SUCCESS;
break;
case SQL_ATTR_CP_MATCH:
ret = SQL_NO_DATA;
break;
case SQL_ATTR_OUTPUT_NTS:
if (val == (SQLPOINTER) SQL_TRUE) {
ret = SQL_SUCCESS;
}
break;
case SQL_ATTR_ODBC_VERSION:
if (!val) {
ret = SQL_ERROR;
break;
}
if (val == (SQLPOINTER) SQL_OV_ODBC2) {
e->ov3 = 0;
ret = SQL_SUCCESS;
}
if (val == (SQLPOINTER) SQL_OV_ODBC3) {
e->ov3 = 1;
ret = SQL_SUCCESS;
}
break;
}
#if defined(_WIN32) || defined(_WIN64)
e->owner = 0;
LeaveCriticalSection(&e->cs);
#endif
return ret;
}
/**
* Internal get error message given handle (HENV, HDBC, or HSTMT).
* @param htype handle type
* @param handle HENV, HDBC, or HSTMT
* @param recno
* @param sqlstate output buffer for SQL state
* @param nativeerr output buffer of native error code
* @param msg output buffer for error message
* @param buflen length of output buffer
* @param msglen output length
* @result ODBC error code
*/
static SQLRETURN
drvgetdiagrec(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno,
SQLCHAR *sqlstate, SQLINTEGER *nativeerr, SQLCHAR *msg,
SQLSMALLINT buflen, SQLSMALLINT *msglen)
{
DBC *d = NULL;
STMT *s = NULL;
int len, naterr;
char *logmsg, *sqlst;
SQLRETURN ret = SQL_ERROR;
if (handle == SQL_NULL_HANDLE) {
return SQL_INVALID_HANDLE;
}
if (sqlstate) {
sqlstate[0] = '\0';
}
if (msg && buflen > 0) {
msg[0] = '\0';
}
if (msglen) {
*msglen = 0;
}
if (nativeerr) {
*nativeerr = 0;
}
switch (htype) {
case SQL_HANDLE_ENV:
case SQL_HANDLE_DESC:
return SQL_NO_DATA;
case SQL_HANDLE_DBC:
HDBC_LOCK((SQLHDBC) handle);
d = (DBC *) handle;
logmsg = (char *) d->logmsg;
sqlst = d->sqlstate;
naterr = d->naterr;
break;
case SQL_HANDLE_STMT:
HSTMT_LOCK((SQLHSTMT) handle);
s = (STMT *) handle;
logmsg = (char *) s->logmsg;
sqlst = s->sqlstate;
naterr = s->naterr;
break;
default:
return SQL_INVALID_HANDLE;
}
if (buflen < 0) {
ret = SQL_ERROR;
goto done;
}
if (recno > 1) {
ret = SQL_NO_DATA;
goto done;
}
len = strlen(logmsg);
if (len == 0) {
ret = SQL_NO_DATA;
goto done;
}
if (nativeerr) {
*nativeerr = naterr;
}
if (sqlstate) {
strcpy((char *) sqlstate, sqlst);
}
if (msglen) {
*msglen = len;
}
if (len >= buflen) {
if (msg && buflen > 0) {
strncpy((char *) msg, logmsg, buflen);
msg[buflen - 1] = '\0';
logmsg[0] = '\0';
}
} else if (msg) {
strcpy((char *) msg, logmsg);
logmsg[0] = '\0';
}
ret = SQL_SUCCESS;
done:
switch (htype) {
case SQL_HANDLE_DBC:
HDBC_UNLOCK((SQLHDBC) handle);
break;
case SQL_HANDLE_STMT:
HSTMT_UNLOCK((SQLHSTMT) handle);
break;
}
return ret;
}
#if !defined(WINTERFACE) || (defined(HAVE_UNIXODBC) && HAVE_UNIXODBC)
/**
* Get error message given handle (HENV, HDBC, or HSTMT).
* @param htype handle type
* @param handle HENV, HDBC, or HSTMT
* @param recno
* @param sqlstate output buffer for SQL state
* @param nativeerr output buffer of native error code
* @param msg output buffer for error message
* @param buflen length of output buffer
* @param msglen output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetDiagRec(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno,
SQLCHAR *sqlstate, SQLINTEGER *nativeerr, SQLCHAR *msg,
SQLSMALLINT buflen, SQLSMALLINT *msglen)
{
return drvgetdiagrec(htype, handle, recno, sqlstate,
nativeerr, msg, buflen, msglen);
}
#endif
#if !defined(HAVE_UNIXODBC) || !HAVE_UNIXODBC
#ifdef WINTERFACE
/**
* Get error message given handle (HENV, HDBC, or HSTMT)
* (UNICODE version).
* @param htype handle type
* @param handle HENV, HDBC, or HSTMT
* @param recno
* @param sqlstate output buffer for SQL state
* @param nativeerr output buffer of native error code
* @param msg output buffer for error message
* @param buflen length of output buffer
* @param msglen output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetDiagRecW(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno,
SQLWCHAR *sqlstate, SQLINTEGER *nativeerr, SQLWCHAR *msg,
SQLSMALLINT buflen, SQLSMALLINT *msglen)
{
char state[16];
SQLSMALLINT len;
SQLRETURN ret;
ret = drvgetdiagrec(htype, handle, recno, (SQLCHAR *) state,
nativeerr, (SQLCHAR *) msg, buflen, &len);
if (ret == SQL_SUCCESS) {
if (sqlstate) {
uc_from_utf_buf((SQLCHAR *) state, -1, sqlstate,
6 * sizeof (SQLWCHAR));
}
if (msg) {
if (len > 0) {
SQLWCHAR *m = NULL;
m = uc_from_utf((unsigned char *) msg, len);
if (m) {
if (buflen) {
buflen /= sizeof (SQLWCHAR);
uc_strncpy(msg, m, buflen);
m[len] = 0;
len = min(buflen, uc_strlen(m));
} else {
len = uc_strlen(m);
}
uc_free(m);
} else {
len = 0;
}
}
if (len <= 0) {
len = 0;
if (buflen > 0) {
msg[0] = 0;
}
}
} else {
/* estimated length !!! */
len *= sizeof (SQLWCHAR);
}
if (msglen) {
*msglen = len;
}
} else if (ret == SQL_NO_DATA) {
if (sqlstate) {
sqlstate[0] = 0;
}
if (msg) {
if (buflen > 0) {
msg[0] = 0;
}
}
if (msglen) {
*msglen = 0;
}
}
return ret;
}
#endif
#endif
/**
* Get error record given handle (HDBC or HSTMT).
* @param htype handle type
* @param handle HDBC or HSTMT
* @param recno diag record number for which info to be retrieved
* @param id diag id for which info to be retrieved
* @param info output buffer for error message
* @param buflen length of output buffer
* @param stringlen output length
* @result ODBC error code
*/
static SQLRETURN
drvgetdiagfield(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno,
SQLSMALLINT id, SQLPOINTER info,
SQLSMALLINT buflen, SQLSMALLINT *stringlen)
{
DBC *d = NULL;
STMT *s = NULL;
int len, naterr;
char *logmsg, *sqlst, *clrmsg = NULL;
SQLRETURN ret = SQL_ERROR;
if (handle == SQL_NULL_HANDLE) {
return SQL_INVALID_HANDLE;
}
if (stringlen) {
*stringlen = 0;
}
switch (htype) {
case SQL_HANDLE_ENV:
case SQL_HANDLE_DESC:
return SQL_NO_DATA;
case SQL_HANDLE_DBC:
HDBC_LOCK((SQLHDBC) handle);
d = (DBC *) handle;
logmsg = (char *) d->logmsg;
sqlst = d->sqlstate;
naterr = d->naterr;
break;
case SQL_HANDLE_STMT:
HSTMT_LOCK((SQLHSTMT) handle);
s = (STMT *) handle;
d = (DBC *) s->dbc;
logmsg = (char *) s->logmsg;
sqlst = s->sqlstate;
naterr = s->naterr;
break;
default:
return SQL_INVALID_HANDLE;
}
if (buflen < 0) {
ret = SQL_ERROR;
goto done;
}
if (recno > 1) {
ret = SQL_NO_DATA;
goto done;
}
switch (id) {
case SQL_DIAG_CLASS_ORIGIN:
logmsg = "ISO 9075";
if (sqlst[0] == 'I' && sqlst[1] == 'M') {
logmsg = "ODBC 3.0";
}
break;
case SQL_DIAG_SUBCLASS_ORIGIN:
logmsg = "ISO 9075";
if (sqlst[0] == 'I' && sqlst[1] == 'M') {
logmsg = "ODBC 3.0";
} else if (sqlst[0] == 'H' && sqlst[1] == 'Y') {
logmsg = "ODBC 3.0";
} else if (sqlst[0] == '2' || sqlst[0] == '0' || sqlst[0] == '4') {
logmsg = "ODBC 3.0";
}
break;
case SQL_DIAG_CONNECTION_NAME:
case SQL_DIAG_SERVER_NAME:
logmsg = d->dsn ? d->dsn : "No DSN";
break;
case SQL_DIAG_SQLSTATE:
logmsg = sqlst;
break;
case SQL_DIAG_MESSAGE_TEXT:
clrmsg = logmsg;
break;
case SQL_DIAG_NUMBER:
naterr = 1;
/* fall through */
case SQL_DIAG_NATIVE:
len = strlen(logmsg);
if (len == 0) {
ret = SQL_NO_DATA;
goto done;
}
if (info) {
*((SQLINTEGER *) info) = naterr;
}
ret = SQL_SUCCESS;
goto done;
default:
ret = SQL_ERROR;
goto done;
}
if (info && buflen > 0) {
((char *) info)[0] = '\0';
}
len = strlen(logmsg);
if (len == 0) {
ret = SQL_NO_DATA;
goto done;
}
if (stringlen) {
*stringlen = len;
}
if (len >= buflen) {
if (info && buflen > 0) {
if (stringlen) {
*stringlen = buflen - 1;
}
strncpy((char *) info, logmsg, buflen);
((char *) info)[buflen - 1] = '\0';
}
} else if (info) {
strcpy((char *) info, logmsg);
}
if (clrmsg) {
*clrmsg = '\0';
}
ret = SQL_SUCCESS;
done:
switch (htype) {
case SQL_HANDLE_DBC:
HDBC_UNLOCK((SQLHDBC) handle);
break;
case SQL_HANDLE_STMT:
HSTMT_UNLOCK((SQLHSTMT) handle);
break;
}
return ret;
}
#ifndef WINTERFACE
/**
* Get error record given handle (HDBC or HSTMT).
* @param htype handle type
* @param handle HDBC or HSTMT
* @param recno diag record number for which info to be retrieved
* @param id diag id for which info to be retrieved
* @param info output buffer for error message
* @param buflen length of output buffer
* @param stringlen output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetDiagField(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno,
SQLSMALLINT id, SQLPOINTER info,
SQLSMALLINT buflen, SQLSMALLINT *stringlen)
{
return drvgetdiagfield(htype, handle, recno, id, info, buflen, stringlen);
}
#endif
#ifdef WINTERFACE
/**
* Get error record given handle (HDBC or HSTMT).
* @param htype handle type
* @param handle HDBC or HSTMT
* @param recno diag record number for which info to be retrieved
* @param id diag id for which info to be retrieved
* @param info output buffer for error message
* @param buflen length of output buffer
* @param stringlen output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetDiagFieldW(SQLSMALLINT htype, SQLHANDLE handle, SQLSMALLINT recno,
SQLSMALLINT id, SQLPOINTER info,
SQLSMALLINT buflen, SQLSMALLINT *stringlen)
{
SQLSMALLINT len;
SQLRETURN ret;
ret = drvgetdiagfield(htype, handle, recno, id, info, buflen, &len);
if (ret == SQL_SUCCESS) {
if (info) {
switch (id) {
case SQL_DIAG_CLASS_ORIGIN:
case SQL_DIAG_SUBCLASS_ORIGIN:
case SQL_DIAG_CONNECTION_NAME:
case SQL_DIAG_SERVER_NAME:
case SQL_DIAG_SQLSTATE:
case SQL_DIAG_MESSAGE_TEXT:
if (len > 0) {
SQLWCHAR *m = NULL;
m = uc_from_utf((unsigned char *) info, len);
if (m) {
if (buflen) {
buflen /= sizeof (SQLWCHAR);
uc_strncpy(info, m, buflen);
m[len] = 0;
len = min(buflen, uc_strlen(m));
} else {
len = uc_strlen(m);
}
uc_free(m);
len *= sizeof (SQLWCHAR);
} else {
len = 0;
}
}
if (len <= 0) {
len = 0;
if (buflen > 0) {
((SQLWCHAR *) info)[0] = 0;
}
}
}
} else {
switch (id) {
case SQL_DIAG_CLASS_ORIGIN:
case SQL_DIAG_SUBCLASS_ORIGIN:
case SQL_DIAG_CONNECTION_NAME:
case SQL_DIAG_SERVER_NAME:
case SQL_DIAG_SQLSTATE:
case SQL_DIAG_MESSAGE_TEXT:
len *= sizeof (SQLWCHAR);
break;
}
}
if (stringlen) {
*stringlen = len;
}
}
return ret;
}
#endif
/**
* Internal get option of HSTMT.
* @param stmt statement handle
* @param attr attribute to be retrieved
* @param val output buffer
* @param bufmax length of output buffer
* @param buflen output length
* @result ODBC error code
*/
static SQLRETURN
drvgetstmtattr(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val,
SQLINTEGER bufmax, SQLINTEGER *buflen)
{
STMT *s = (STMT *) stmt;
DBC *d = (DBC *) s->dbc;
SQLUINTEGER *uval = (SQLUINTEGER *) val;
switch (attr) {
case SQL_QUERY_TIMEOUT:
*uval = 0;
return SQL_SUCCESS;
case SQL_ATTR_CURSOR_TYPE:
*uval = s->curtype;
return SQL_SUCCESS;
case SQL_ATTR_CURSOR_SCROLLABLE:
*uval = (s->curtype != SQL_CURSOR_FORWARD_ONLY) ?
SQL_SCROLLABLE : SQL_NONSCROLLABLE;
return SQL_SUCCESS;
#ifdef SQL_ATTR_CURSOR_SENSITIVITY
case SQL_ATTR_CURSOR_SENSITIVITY:
*uval = SQL_UNSPECIFIED;
return SQL_SUCCESS;
#endif
case SQL_ATTR_ROW_NUMBER:
if (s == d->vm_stmt) {
*uval = (d->vm_rownum < 0) ?
SQL_ROW_NUMBER_UNKNOWN : (d->vm_rownum + 1);
} else {
*uval = (s->rowp < 0) ? SQL_ROW_NUMBER_UNKNOWN : (s->rowp + 1);
}
return SQL_SUCCESS;
case SQL_ATTR_ASYNC_ENABLE:
*uval = SQL_ASYNC_ENABLE_OFF;
return SQL_SUCCESS;
case SQL_CONCURRENCY:
*uval = SQL_CONCUR_LOCK;
return SQL_SUCCESS;
case SQL_ATTR_RETRIEVE_DATA:
*uval = s->retr_data;
return SQL_SUCCESS;
case SQL_ROWSET_SIZE:
case SQL_ATTR_ROW_ARRAY_SIZE:
*uval = s->rowset_size;
return SQL_SUCCESS;
/* Needed for some driver managers, but dummies for now */
case SQL_ATTR_IMP_ROW_DESC:
case SQL_ATTR_APP_ROW_DESC:
case SQL_ATTR_IMP_PARAM_DESC:
case SQL_ATTR_APP_PARAM_DESC:
*((SQLHDESC *) val) = (SQLHDESC) DEAD_MAGIC;
return SQL_SUCCESS;
case SQL_ATTR_ROW_STATUS_PTR:
*((SQLUSMALLINT **) val) = s->row_status;
return SQL_SUCCESS;
case SQL_ATTR_ROWS_FETCHED_PTR:
*((SQLUINTEGER **) val) = s->row_count;
return SQL_SUCCESS;
case SQL_ATTR_USE_BOOKMARKS: {
STMT *s = (STMT *) stmt;
*(SQLUINTEGER *) val = s->bkmrk ? SQL_UB_ON : SQL_UB_OFF;
return SQL_SUCCESS;
}
case SQL_ATTR_PARAM_BIND_OFFSET_PTR:
*((SQLUINTEGER **) val) = s->parm_bind_offs;
return SQL_SUCCESS;
case SQL_ATTR_PARAM_BIND_TYPE:
*((SQLUINTEGER *) val) = SQL_PARAM_BIND_BY_COLUMN;
return SQL_SUCCESS;
case SQL_ATTR_PARAM_OPERATION_PTR:
*((SQLUSMALLINT **) val) = s->parm_oper;
return SQL_SUCCESS;
case SQL_ATTR_PARAM_STATUS_PTR:
*((SQLUSMALLINT **) val) = s->parm_status;
return SQL_SUCCESS;
case SQL_ATTR_PARAMS_PROCESSED_PTR:
*((SQLUINTEGER **) val) = s->parm_proc;
return SQL_SUCCESS;
case SQL_ATTR_PARAMSET_SIZE:
*((SQLUINTEGER *) val) = s->paramset_size;
return SQL_SUCCESS;
case SQL_ATTR_ROW_BIND_TYPE:
*(SQLUINTEGER *) val = s->bind_type;
return SQL_SUCCESS;
case SQL_ATTR_ROW_BIND_OFFSET_PTR:
*((SQLUINTEGER **) val) = s->bind_offs;
return SQL_SUCCESS;
case SQL_ATTR_NOSCAN:
*((SQLUINTEGER **) val) = SQL_NOSCAN_OFF;
return SQL_SUCCESS;
case SQL_ATTR_MAX_ROWS:
case SQL_ATTR_MAX_LENGTH:
*((SQLINTEGER *) val) = 1000000000;
return SQL_SUCCESS;
}
return drvunimplstmt(stmt);
}
#if (defined(HAVE_UNIXODBC) && HAVE_UNIXODBC) || !defined(WINTERFACE)
/**
* Get option of HSTMT.
* @param stmt statement handle
* @param attr attribute to be retrieved
* @param val output buffer
* @param bufmax length of output buffer
* @param buflen output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetStmtAttr(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val,
SQLINTEGER bufmax, SQLINTEGER *buflen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvgetstmtattr(stmt, attr, val, bufmax, buflen);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Get option of HSTMT (UNICODE version).
* @param stmt statement handle
* @param attr attribute to be retrieved
* @param val output buffer
* @param bufmax length of output buffer
* @param buflen output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetStmtAttrW(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val,
SQLINTEGER bufmax, SQLINTEGER *buflen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvgetstmtattr(stmt, attr, val, bufmax, buflen);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
/**
* Internal set option on HSTMT.
* @param stmt statement handle
* @param attr attribute to be set
* @param val input buffer (attribute value)
* @param buflen length of input buffer
* @result ODBC error code
*/
static SQLRETURN
drvsetstmtattr(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val,
SQLINTEGER buflen)
{
STMT *s = (STMT *) stmt;
switch (attr) {
case SQL_ATTR_CURSOR_TYPE:
if (val == (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY) {
s->curtype = SQL_CURSOR_FORWARD_ONLY;
} else {
s->curtype = SQL_CURSOR_STATIC;
}
if (val != (SQLPOINTER) SQL_CURSOR_FORWARD_ONLY &&
val != (SQLPOINTER) SQL_CURSOR_STATIC) {
goto e01s02;
}
return SQL_SUCCESS;
case SQL_ATTR_CURSOR_SCROLLABLE:
if (val == (SQLPOINTER) SQL_NONSCROLLABLE) {
s->curtype = SQL_CURSOR_FORWARD_ONLY;
} else {
s->curtype = SQL_CURSOR_STATIC;
}
return SQL_SUCCESS;
case SQL_ATTR_ASYNC_ENABLE:
if (val != (SQLPOINTER) SQL_ASYNC_ENABLE_OFF) {
e01s02:
setstat(s, -1, "option value changed", "01S02");
return SQL_SUCCESS_WITH_INFO;
}
return SQL_SUCCESS;
case SQL_CONCURRENCY:
if (val != (SQLPOINTER) SQL_CONCUR_LOCK) {
goto e01s02;
}
return SQL_SUCCESS;
#ifdef SQL_ATTR_CURSOR_SENSITIVITY
case SQL_ATTR_CURSOR_SENSITIVITY:
if (val != (SQLPOINTER) SQL_UNSPECIFIED) {
goto e01s02;
}
return SQL_SUCCESS;
#endif
case SQL_ATTR_QUERY_TIMEOUT:
return SQL_SUCCESS;
case SQL_ATTR_RETRIEVE_DATA:
if (val != (SQLPOINTER) SQL_RD_ON &&
val != (SQLPOINTER) SQL_RD_OFF) {
goto e01s02;
}
s->retr_data = (PTRDIFF_T) val;
return SQL_SUCCESS;
case SQL_ROWSET_SIZE:
case SQL_ATTR_ROW_ARRAY_SIZE:
if ((PTRDIFF_T) val < 1) {
setstat(s, -1, "invalid rowset size", "HY000");
return SQL_ERROR;
} else {
SQLUSMALLINT *rst = &s->row_status1;
if ((PTRDIFF_T) val > 1) {
rst = xmalloc(sizeof (SQLUSMALLINT) * (PTRDIFF_T) val);
if (!rst) {
return nomem(s);
}
}
if (s->row_status0 != &s->row_status1) {
freep(&s->row_status0);
}
s->row_status0 = rst;
s->rowset_size = (PTRDIFF_T) val;
}
return SQL_SUCCESS;
case SQL_ATTR_ROW_STATUS_PTR:
s->row_status = (SQLUSMALLINT *) val;
return SQL_SUCCESS;
case SQL_ATTR_ROWS_FETCHED_PTR:
s->row_count = (SQLUINTEGER *) val;
return SQL_SUCCESS;
case SQL_ATTR_PARAM_BIND_OFFSET_PTR:
s->parm_bind_offs = (SQLUINTEGER *) val;
return SQL_SUCCESS;
case SQL_ATTR_PARAM_BIND_TYPE:
s->parm_bind_type = (PTRDIFF_T) val;
return SQL_SUCCESS;
case SQL_ATTR_PARAM_OPERATION_PTR:
s->parm_oper = (SQLUSMALLINT *) val;
return SQL_SUCCESS;
case SQL_ATTR_PARAM_STATUS_PTR:
s->parm_status = (SQLUSMALLINT *) val;
return SQL_SUCCESS;
case SQL_ATTR_PARAMS_PROCESSED_PTR:
s->parm_proc = (SQLUINTEGER *) val;
return SQL_SUCCESS;
case SQL_ATTR_PARAMSET_SIZE:
if ((PTRDIFF_T) val < 1) {
goto e01s02;
}
s->paramset_size = (PTRDIFF_T) val;
s->paramset_count = 0;
return SQL_SUCCESS;
case SQL_ATTR_ROW_BIND_TYPE:
s->bind_type = (PTRDIFF_T) val;
return SQL_SUCCESS;
case SQL_ATTR_ROW_BIND_OFFSET_PTR:
s->bind_offs = (SQLUINTEGER *) val;
return SQL_SUCCESS;
case SQL_ATTR_USE_BOOKMARKS:
if (val != (SQLPOINTER) SQL_UB_OFF &&
val != (SQLPOINTER) SQL_UB_ON) {
goto e01s02;
}
s->bkmrk = val == (SQLPOINTER) SQL_UB_ON;
return SQL_SUCCESS;
case SQL_ATTR_NOSCAN:
if (val != (SQLPOINTER) SQL_NOSCAN_OFF) {
goto e01s02;
}
return SQL_SUCCESS;
case SQL_ATTR_MAX_ROWS:
case SQL_ATTR_MAX_LENGTH:
if (val != (SQLPOINTER) 1000000000) {
goto e01s02;
}
return SQL_SUCCESS;
}
return drvunimplstmt(stmt);
}
#if (defined(HAVE_UNIXODBC) && HAVE_UNIXODBC) || !defined(WINTERFACE)
/**
* Set option on HSTMT.
* @param stmt statement handle
* @param attr attribute to be set
* @param val input buffer (attribute value)
* @param buflen length of input buffer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSetStmtAttr(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val,
SQLINTEGER buflen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvsetstmtattr(stmt, attr, val, buflen);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Set option on HSTMT (UNICODE version).
* @param stmt statement handle
* @param attr attribute to be set
* @param val input buffer (attribute value)
* @param buflen length of input buffer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSetStmtAttrW(SQLHSTMT stmt, SQLINTEGER attr, SQLPOINTER val,
SQLINTEGER buflen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvsetstmtattr(stmt, attr, val, buflen);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
/**
* Internal get option of HSTMT.
* @param stmt statement handle
* @param opt option to be retrieved
* @param param output buffer
* @result ODBC error code
*/
static SQLRETURN
drvgetstmtoption(SQLHSTMT stmt, SQLUSMALLINT opt, SQLPOINTER param)
{
STMT *s = (STMT *) stmt;
DBC *d = (DBC *) s->dbc;
SQLUINTEGER *ret = (SQLUINTEGER *) param;
switch (opt) {
case SQL_QUERY_TIMEOUT:
*ret = 0;
return SQL_SUCCESS;
case SQL_CURSOR_TYPE:
*ret = s->curtype;
return SQL_SUCCESS;
case SQL_ROW_NUMBER:
if (s == d->vm_stmt) {
*ret = (d->vm_rownum < 0) ?
SQL_ROW_NUMBER_UNKNOWN : (d->vm_rownum + 1);
} else {
*ret = (s->rowp < 0) ? SQL_ROW_NUMBER_UNKNOWN : (s->rowp + 1);
}
return SQL_SUCCESS;
case SQL_ASYNC_ENABLE:
*ret = SQL_ASYNC_ENABLE_OFF;
return SQL_SUCCESS;
case SQL_CONCURRENCY:
*ret = SQL_CONCUR_LOCK;
return SQL_SUCCESS;
case SQL_ATTR_RETRIEVE_DATA:
*ret = s->retr_data;
return SQL_SUCCESS;
case SQL_ROWSET_SIZE:
case SQL_ATTR_ROW_ARRAY_SIZE:
*ret = s->rowset_size;
return SQL_SUCCESS;
case SQL_ATTR_NOSCAN:
*ret = SQL_NOSCAN_OFF;
return SQL_SUCCESS;
case SQL_ATTR_MAX_ROWS:
case SQL_ATTR_MAX_LENGTH:
*ret = 1000000000;
return SQL_SUCCESS;
}
return drvunimplstmt(stmt);
}
/**
* Get option of HSTMT.
* @param stmt statement handle
* @param opt option to be retrieved
* @param param output buffer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetStmtOption(SQLHSTMT stmt, SQLUSMALLINT opt, SQLPOINTER param)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvgetstmtoption(stmt, opt, param);
HSTMT_UNLOCK(stmt);
return ret;
}
#ifdef WINTERFACE
/**
* Get option of HSTMT (UNICODE version).
* @param stmt statement handle
* @param opt option to be retrieved
* @param param output buffer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetStmtOptionW(SQLHSTMT stmt, SQLUSMALLINT opt, SQLPOINTER param)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvgetstmtoption(stmt, opt, param);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
/**
* Internal set option on HSTMT.
* @param stmt statement handle
* @param opt option to be set
* @param param input buffer (option value)
* @result ODBC error code
*/
static SQLRETURN
drvsetstmtoption(SQLHSTMT stmt, SQLUSMALLINT opt, SQLUINTEGER param)
{
STMT *s = (STMT *) stmt;
switch (opt) {
case SQL_CURSOR_TYPE:
if (param == SQL_CURSOR_FORWARD_ONLY) {
s->curtype = param;
} else {
s->curtype = SQL_CURSOR_STATIC;
}
if (param != SQL_CURSOR_FORWARD_ONLY &&
param != SQL_CURSOR_STATIC) {
goto e01s02;
}
return SQL_SUCCESS;
case SQL_ASYNC_ENABLE:
if (param != SQL_ASYNC_ENABLE_OFF) {
goto e01s02;
}
return SQL_SUCCESS;
case SQL_CONCURRENCY:
if (param != SQL_CONCUR_LOCK) {
goto e01s02;
}
return SQL_SUCCESS;
case SQL_QUERY_TIMEOUT:
return SQL_SUCCESS;
case SQL_RETRIEVE_DATA:
if (param != SQL_RD_ON && param != SQL_RD_OFF) {
e01s02:
setstat(s, -1, "option value changed", "01S02");
return SQL_SUCCESS_WITH_INFO;
}
s->retr_data = (int) param;
return SQL_SUCCESS;
case SQL_ROWSET_SIZE:
case SQL_ATTR_ROW_ARRAY_SIZE:
if (param < 1) {
setstat(s, -1, "invalid rowset size", "HY000");
return SQL_ERROR;
} else {
SQLUSMALLINT *rst = &s->row_status1;
if (param > 1) {
rst = xmalloc(sizeof (SQLUSMALLINT) * param);
if (!rst) {
return nomem(s);
}
}
if (s->row_status0 != &s->row_status1) {
freep(&s->row_status0);
}
s->row_status0 = rst;
s->rowset_size = param;
}
return SQL_SUCCESS;
case SQL_ATTR_NOSCAN:
if (param != SQL_NOSCAN_OFF) {
goto e01s02;
}
return SQL_SUCCESS;
case SQL_ATTR_MAX_ROWS:
case SQL_ATTR_MAX_LENGTH:
if (param != 1000000000) {
goto e01s02;
}
return SQL_SUCCESS;
}
return drvunimplstmt(stmt);
}
/**
* Set option on HSTMT.
* @param stmt statement handle
* @param opt option to be set
* @param param input buffer (option value)
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSetStmtOption(SQLHSTMT stmt, SQLUSMALLINT opt,
SETSTMTOPTION_LAST_ARG_TYPE param)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvsetstmtoption(stmt, opt, (SQLUINTEGER) param);
HSTMT_UNLOCK(stmt);
return ret;
}
#ifdef WINTERFACE
/**
* Set option on HSTMT (UNICODE version).
* @param stmt statement handle
* @param opt option to be set
* @param param input buffer (option value)
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSetStmtOptionW(SQLHSTMT stmt, SQLUSMALLINT opt,
SETSTMTOPTION_LAST_ARG_TYPE param)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvsetstmtoption(stmt, opt, (SQLUINTEGER) param);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
/*
* Internal set position on result in HSTMT.
* @param stmt statement handle
* @param row row to be positioned
* @param op operation code
* @param lock locking type
* @result ODBC error code
*/
static SQLRETURN
drvsetpos(SQLHSTMT stmt, SQLSETPOSIROW row, SQLUSMALLINT op, SQLUSMALLINT lock)
{
STMT *s = (STMT *) stmt;
if (op != SQL_POSITION) {
return drvunimplstmt(stmt);
}
if (!s->rows || row <= 0 || row > s->nrows) {
setstat(s, -1, "row out of range", (*s->ov3) ? "HY107" : "S1107");
return SQL_ERROR;
}
s->rowp = row - 1;
return SQL_SUCCESS;
}
/**
* Set position on result in HSTMT.
* @param stmt statement handle
* @param row row to be positioned
* @param op operation code
* @param lock locking type
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSetPos(SQLHSTMT stmt, SQLSETPOSIROW row, SQLUSMALLINT op, SQLUSMALLINT lock)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvsetpos(stmt, row, op, lock);
HSTMT_UNLOCK(stmt);
return ret;
}
/**
* Function not implemented.
*/
SQLRETURN SQL_API
SQLSetScrollOptions(SQLHSTMT stmt, SQLUSMALLINT concur, SQLLEN rowkeyset,
SQLUSMALLINT rowset)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvunimplstmt(stmt);
HSTMT_UNLOCK(stmt);
return ret;
}
#define strmak(dst, src, max, lenp) { \
int len = strlen(src); \
int cnt = min(len + 1, max); \
strncpy(dst, src, cnt); \
*lenp = (cnt > len) ? len : cnt; \
}
/**
* Internal return information about what this ODBC driver supports.
* @param dbc database connection handle
* @param type type of information to be retrieved
* @param val output buffer
* @param valMax length of output buffer
* @param valLen output length
* @result ODBC error code
*/
static SQLRETURN
drvgetinfo(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax,
SQLSMALLINT *valLen)
{
DBC *d;
char dummyc[16];
SQLSMALLINT dummy;
#if defined(_WIN32) || defined(_WIN64)
char drvname[301];
#else
static char drvname[] =
#ifdef __OS2__
"SQLLODBC.DLL";
#else
"sqliteodbc.so";
#endif
#endif
if (dbc == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
d = (DBC *) dbc;
if (valMax) {
valMax--;
}
if (!valLen) {
valLen = &dummy;
}
if (!val) {
val = dummyc;
valMax = sizeof (dummyc) - 1;
}
switch (type) {
case SQL_MAX_USER_NAME_LEN:
*((SQLSMALLINT *) val) = 16;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_USER_NAME:
strmak(val, "", valMax, valLen);
break;
case SQL_DRIVER_ODBC_VER:
#if 0
strmak(val, (*d->ov3) ? "03.00" : "02.50", valMax, valLen);
#else
strmak(val, "03.00", valMax, valLen);
#endif
break;
case SQL_ACTIVE_CONNECTIONS:
case SQL_ACTIVE_STATEMENTS:
*((SQLSMALLINT *) val) = 0;
*valLen = sizeof (SQLSMALLINT);
break;
#ifdef SQL_ASYNC_MODE
case SQL_ASYNC_MODE:
*((SQLUINTEGER *) val) = SQL_AM_NONE;
*valLen = sizeof (SQLUINTEGER);
break;
#endif
#ifdef SQL_CREATE_TABLE
case SQL_CREATE_TABLE:
*((SQLUINTEGER *) val) = SQL_CT_CREATE_TABLE |
SQL_CT_COLUMN_DEFAULT |
SQL_CT_COLUMN_CONSTRAINT |
SQL_CT_CONSTRAINT_NON_DEFERRABLE;
*valLen = sizeof (SQLUINTEGER);
break;
#endif
#ifdef SQL_CREATE_VIEW
case SQL_CREATE_VIEW:
*((SQLUINTEGER *) val) = SQL_CV_CREATE_VIEW;
*valLen = sizeof (SQLUINTEGER);
break;
#endif
#ifdef SQL_DDL_INDEX
case SQL_DDL_INDEX:
*((SQLUINTEGER *) val) = SQL_DI_CREATE_INDEX | SQL_DI_DROP_INDEX;
*valLen = sizeof (SQLUINTEGER);
break;
#endif
#ifdef SQL_DROP_TABLE
case SQL_DROP_TABLE:
*((SQLUINTEGER *) val) = SQL_DT_DROP_TABLE;
*valLen = sizeof (SQLUINTEGER);
break;
#endif
#ifdef SQL_DROP_VIEW
case SQL_DROP_VIEW:
*((SQLUINTEGER *) val) = SQL_DV_DROP_VIEW;
*valLen = sizeof (SQLUINTEGER);
break;
#endif
#ifdef SQL_INDEX_KEYWORDS
case SQL_INDEX_KEYWORDS:
*((SQLUINTEGER *) val) = SQL_IK_ALL;
*valLen = sizeof (SQLUINTEGER);
break;
#endif
case SQL_DATA_SOURCE_NAME:
strmak(val, d->dsn ? d->dsn : "", valMax, valLen);
break;
case SQL_DRIVER_NAME:
#if defined(_WIN32) || defined(_WIN64)
GetModuleFileName(hModule, drvname, sizeof (drvname));
#endif
strmak(val, drvname, valMax, valLen);
break;
case SQL_DRIVER_VER:
strmak(val, DRIVER_VER_INFO, valMax, valLen);
break;
case SQL_FETCH_DIRECTION:
*((SQLUINTEGER *) val) = SQL_FD_FETCH_NEXT | SQL_FD_FETCH_FIRST |
SQL_FD_FETCH_LAST | SQL_FD_FETCH_PRIOR | SQL_FD_FETCH_ABSOLUTE;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_ODBC_VER:
strmak(val, (*d->ov3) ? "03.00" : "02.50", valMax, valLen);
break;
case SQL_ODBC_SAG_CLI_CONFORMANCE:
*((SQLSMALLINT *) val) = SQL_OSCC_NOT_COMPLIANT;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_STANDARD_CLI_CONFORMANCE:
*((SQLUINTEGER *) val) = SQL_SCC_XOPEN_CLI_VERSION1;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_SERVER_NAME:
case SQL_DATABASE_NAME:
strmak(val, d->dbname ? d->dbname : "", valMax, valLen);
break;
case SQL_SEARCH_PATTERN_ESCAPE:
strmak(val, "\\", valMax, valLen);
break;
case SQL_ODBC_SQL_CONFORMANCE:
*((SQLSMALLINT *) val) = SQL_OSC_MINIMUM;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_ODBC_API_CONFORMANCE:
*((SQLSMALLINT *) val) = SQL_OAC_LEVEL1;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_DBMS_NAME:
strmak(val, "SQLite", valMax, valLen);
break;
case SQL_DBMS_VER:
strmak(val, SQLITE_VERSION, valMax, valLen);
break;
case SQL_COLUMN_ALIAS:
case SQL_NEED_LONG_DATA_LEN:
strmak(val, "Y", valMax, valLen);
break;
case SQL_ROW_UPDATES:
case SQL_ACCESSIBLE_PROCEDURES:
case SQL_PROCEDURES:
case SQL_EXPRESSIONS_IN_ORDERBY:
case SQL_ODBC_SQL_OPT_IEF:
case SQL_LIKE_ESCAPE_CLAUSE:
case SQL_ORDER_BY_COLUMNS_IN_SELECT:
case SQL_OUTER_JOINS:
case SQL_ACCESSIBLE_TABLES:
case SQL_MULT_RESULT_SETS:
case SQL_MULTIPLE_ACTIVE_TXN:
case SQL_MAX_ROW_SIZE_INCLUDES_LONG:
strmak(val, "N", valMax, valLen);
break;
#ifdef SQL_CATALOG_NAME
case SQL_CATALOG_NAME:
#if defined(_WIN32) || defined(_WIN64)
strmak(val, d->xcelqrx ? "Y": "N", valMax, valLen);
#else
strmak(val, "N", valMax, valLen);
#endif
break;
#endif
case SQL_DATA_SOURCE_READ_ONLY:
strmak(val, "N", valMax, valLen);
break;
#ifdef SQL_OJ_CAPABILITIES
case SQL_OJ_CAPABILITIES:
*((SQLUINTEGER *) val) = 0;
*valLen = sizeof (SQLUINTEGER);
break;
#endif
#ifdef SQL_MAX_IDENTIFIER_LEN
case SQL_MAX_IDENTIFIER_LEN:
*((SQLUSMALLINT *) val) = 255;
*valLen = sizeof (SQLUSMALLINT);
break;
#endif
case SQL_CONCAT_NULL_BEHAVIOR:
*((SQLSMALLINT *) val) = SQL_CB_NULL;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_CURSOR_COMMIT_BEHAVIOR:
case SQL_CURSOR_ROLLBACK_BEHAVIOR:
*((SQLSMALLINT *) val) = SQL_CB_PRESERVE;
*valLen = sizeof (SQLSMALLINT);
break;
#ifdef SQL_CURSOR_SENSITIVITY
case SQL_CURSOR_SENSITIVITY:
*((SQLUINTEGER *) val) = SQL_UNSPECIFIED;
*valLen = sizeof (SQLUINTEGER);
break;
#endif
case SQL_DEFAULT_TXN_ISOLATION:
*((SQLUINTEGER *) val) = SQL_TXN_SERIALIZABLE;
*valLen = sizeof (SQLUINTEGER);
break;
#ifdef SQL_DESCRIBE_PARAMETER
case SQL_DESCRIBE_PARAMETER:
strmak(val, "Y", valMax, valLen);
break;
#endif
case SQL_TXN_ISOLATION_OPTION:
*((SQLUINTEGER *) val) = SQL_TXN_SERIALIZABLE;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_IDENTIFIER_CASE:
*((SQLSMALLINT *) val) = SQL_IC_SENSITIVE;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_IDENTIFIER_QUOTE_CHAR:
strmak(val, "\"", valMax, valLen);
break;
case SQL_MAX_TABLE_NAME_LEN:
case SQL_MAX_COLUMN_NAME_LEN:
*((SQLSMALLINT *) val) = 255;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_MAX_CURSOR_NAME_LEN:
*((SWORD *) val) = 255;
*valLen = sizeof (SWORD);
break;
case SQL_MAX_PROCEDURE_NAME_LEN:
*((SQLSMALLINT *) val) = 0;
break;
case SQL_MAX_QUALIFIER_NAME_LEN:
case SQL_MAX_OWNER_NAME_LEN:
*((SQLSMALLINT *) val) = 255;
break;
case SQL_OWNER_TERM:
strmak(val, "", valMax, valLen);
break;
case SQL_PROCEDURE_TERM:
strmak(val, "PROCEDURE", valMax, valLen);
break;
case SQL_QUALIFIER_NAME_SEPARATOR:
strmak(val, ".", valMax, valLen);
break;
case SQL_QUALIFIER_TERM:
#if defined(_WIN32) || defined(_WIN64)
strmak(val, d->xcelqrx ? "CATALOG" : "", valMax, valLen);
#else
strmak(val, "", valMax, valLen);
#endif
break;
case SQL_QUALIFIER_USAGE:
#if defined(_WIN32) || defined(_WIN64)
*((SQLUINTEGER *) val) = d->xcelqrx ?
(SQL_CU_DML_STATEMENTS | SQL_CU_INDEX_DEFINITION |
SQL_CU_TABLE_DEFINITION) : 0;
#else
*((SQLUINTEGER *) val) = 0;
#endif
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_SCROLL_CONCURRENCY:
*((SQLUINTEGER *) val) = SQL_SCCO_LOCK;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_SCROLL_OPTIONS:
*((SQLUINTEGER *) val) = SQL_SO_STATIC | SQL_SO_FORWARD_ONLY;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_TABLE_TERM:
strmak(val, "TABLE", valMax, valLen);
break;
case SQL_TXN_CAPABLE:
*((SQLSMALLINT *) val) = SQL_TC_ALL;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_CONVERT_FUNCTIONS:
*((SQLUINTEGER *) val) = 0;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_SYSTEM_FUNCTIONS:
case SQL_NUMERIC_FUNCTIONS:
case SQL_STRING_FUNCTIONS:
case SQL_TIMEDATE_FUNCTIONS:
*((SQLUINTEGER *) val) = 0;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_CONVERT_BIGINT:
case SQL_CONVERT_BIT:
case SQL_CONVERT_CHAR:
case SQL_CONVERT_DATE:
case SQL_CONVERT_DECIMAL:
case SQL_CONVERT_DOUBLE:
case SQL_CONVERT_FLOAT:
case SQL_CONVERT_INTEGER:
case SQL_CONVERT_LONGVARCHAR:
case SQL_CONVERT_NUMERIC:
case SQL_CONVERT_REAL:
case SQL_CONVERT_SMALLINT:
case SQL_CONVERT_TIME:
case SQL_CONVERT_TIMESTAMP:
case SQL_CONVERT_TINYINT:
case SQL_CONVERT_VARCHAR:
*((SQLUINTEGER *) val) =
SQL_CVT_CHAR | SQL_CVT_NUMERIC | SQL_CVT_DECIMAL |
SQL_CVT_INTEGER | SQL_CVT_SMALLINT | SQL_CVT_FLOAT | SQL_CVT_REAL |
SQL_CVT_DOUBLE | SQL_CVT_VARCHAR | SQL_CVT_LONGVARCHAR |
SQL_CVT_BIT | SQL_CVT_TINYINT | SQL_CVT_BIGINT |
SQL_CVT_DATE | SQL_CVT_TIME | SQL_CVT_TIMESTAMP;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_CONVERT_BINARY:
case SQL_CONVERT_VARBINARY:
case SQL_CONVERT_LONGVARBINARY:
*((SQLUINTEGER *) val) = 0;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_POSITIONED_STATEMENTS:
case SQL_LOCK_TYPES:
*((SQLUINTEGER *) val) = 0;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_BOOKMARK_PERSISTENCE:
*((SQLUINTEGER *) val) = SQL_BP_SCROLL;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_UNION:
*((SQLUINTEGER *) val) = SQL_U_UNION | SQL_U_UNION_ALL;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_OWNER_USAGE:
case SQL_SUBQUERIES:
case SQL_TIMEDATE_ADD_INTERVALS:
case SQL_TIMEDATE_DIFF_INTERVALS:
*((SQLUINTEGER *) val) = 0;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_QUOTED_IDENTIFIER_CASE:
*((SQLUSMALLINT *) val) = SQL_IC_SENSITIVE;
*valLen = sizeof (SQLUSMALLINT);
break;
case SQL_POS_OPERATIONS:
*((SQLUINTEGER *) val) = 0;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_ALTER_TABLE:
*((SQLUINTEGER *) val) = 0;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_CORRELATION_NAME:
*((SQLSMALLINT *) val) = SQL_CN_DIFFERENT;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_NON_NULLABLE_COLUMNS:
*((SQLSMALLINT *) val) = SQL_NNC_NON_NULL;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_NULL_COLLATION:
*((SQLSMALLINT *) val) = SQL_NC_START;
*valLen = sizeof(SQLSMALLINT);
break;
case SQL_MAX_COLUMNS_IN_GROUP_BY:
case SQL_MAX_COLUMNS_IN_ORDER_BY:
case SQL_MAX_COLUMNS_IN_SELECT:
case SQL_MAX_COLUMNS_IN_TABLE:
case SQL_MAX_ROW_SIZE:
case SQL_MAX_TABLES_IN_SELECT:
*((SQLSMALLINT *) val) = 0;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_MAX_BINARY_LITERAL_LEN:
case SQL_MAX_CHAR_LITERAL_LEN:
*((SQLUINTEGER *) val) = 0;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_MAX_COLUMNS_IN_INDEX:
*((SQLSMALLINT *) val) = 0;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_MAX_INDEX_SIZE:
*((SQLUINTEGER *) val) = 0;
*valLen = sizeof(SQLUINTEGER);
break;
#ifdef SQL_MAX_IDENTIFIER_LENGTH
case SQL_MAX_IDENTIFIER_LENGTH:
*((SQLUINTEGER *) val) = 255;
*valLen = sizeof (SQLUINTEGER);
break;
#endif
case SQL_MAX_STATEMENT_LEN:
*((SQLUINTEGER *) val) = 16384;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_QUALIFIER_LOCATION:
*((SQLSMALLINT *) val) = SQL_QL_START;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_GETDATA_EXTENSIONS:
*((SQLUINTEGER *) val) =
SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BOUND;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_STATIC_SENSITIVITY:
*((SQLUINTEGER *) val) = 0;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_FILE_USAGE:
#if defined(_WIN32) || defined(_WIN64)
*((SQLSMALLINT *) val) =
d->xcelqrx ? SQL_FILE_CATALOG : SQL_FILE_NOT_SUPPORTED;
#else
*((SQLSMALLINT *) val) = SQL_FILE_NOT_SUPPORTED;
#endif
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_GROUP_BY:
*((SQLSMALLINT *) val) = 0;
*valLen = sizeof (SQLSMALLINT);
break;
case SQL_KEYWORDS:
strmak(val, "CREATE,SELECT,DROP,DELETE,UPDATE,INSERT,"
"INTO,VALUES,TABLE,INDEX,FROM,SET,WHERE,AND,CURRENT,OF",
valMax, valLen);
break;
case SQL_SPECIAL_CHARACTERS:
#ifdef SQL_COLLATION_SEQ
case SQL_COLLATION_SEQ:
#endif
strmak(val, "", valMax, valLen);
break;
case SQL_BATCH_SUPPORT:
case SQL_BATCH_ROW_COUNT:
case SQL_PARAM_ARRAY_ROW_COUNTS:
*((SQLUINTEGER *) val) = 0;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES1:
*((SQLUINTEGER *) val) = SQL_CA1_NEXT | SQL_CA1_BOOKMARK;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_STATIC_CURSOR_ATTRIBUTES1:
*((SQLUINTEGER *) val) = SQL_CA1_NEXT | SQL_CA1_ABSOLUTE |
SQL_CA1_RELATIVE | SQL_CA1_BOOKMARK;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_FORWARD_ONLY_CURSOR_ATTRIBUTES2:
case SQL_STATIC_CURSOR_ATTRIBUTES2:
*((SQLUINTEGER *) val) = SQL_CA2_READ_ONLY_CONCURRENCY |
SQL_CA2_LOCK_CONCURRENCY;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_KEYSET_CURSOR_ATTRIBUTES1:
case SQL_KEYSET_CURSOR_ATTRIBUTES2:
case SQL_DYNAMIC_CURSOR_ATTRIBUTES1:
case SQL_DYNAMIC_CURSOR_ATTRIBUTES2:
*((SQLUINTEGER *) val) = 0;
*valLen = sizeof (SQLUINTEGER);
break;
case SQL_ODBC_INTERFACE_CONFORMANCE:
*((SQLUINTEGER *) val) = SQL_OIC_CORE;
*valLen = sizeof (SQLUINTEGER);
break;
default:
setstatd(d, -1, "unsupported info option %d",
(*d->ov3) ? "HYC00" : "S1C00", type);
return SQL_ERROR;
}
return SQL_SUCCESS;
}
#if (defined(HAVE_UNIXODBC) && HAVE_UNIXODBC) || !defined(WINTERFACE)
/**
* Return information about what this ODBC driver supports.
* @param dbc database connection handle
* @param type type of information to be retrieved
* @param val output buffer
* @param valMax length of output buffer
* @param valLen output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetInfo(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax,
SQLSMALLINT *valLen)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvgetinfo(dbc, type, val, valMax, valLen);
HDBC_UNLOCK(dbc);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Return information about what this ODBC driver supports.
* @param dbc database connection handle
* @param type type of information to be retrieved
* @param val output buffer
* @param valMax length of output buffer
* @param valLen output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetInfoW(SQLHDBC dbc, SQLUSMALLINT type, SQLPOINTER val, SQLSMALLINT valMax,
SQLSMALLINT *valLen)
{
SQLRETURN ret;
SQLSMALLINT len = 0;
HDBC_LOCK(dbc);
ret = drvgetinfo(dbc, type, val, valMax, &len);
HDBC_UNLOCK(dbc);
if (ret == SQL_SUCCESS) {
SQLWCHAR *v = NULL;
switch (type) {
case SQL_USER_NAME:
case SQL_DRIVER_ODBC_VER:
case SQL_DATA_SOURCE_NAME:
case SQL_DRIVER_NAME:
case SQL_DRIVER_VER:
case SQL_ODBC_VER:
case SQL_SERVER_NAME:
case SQL_DATABASE_NAME:
case SQL_SEARCH_PATTERN_ESCAPE:
case SQL_DBMS_NAME:
case SQL_DBMS_VER:
case SQL_NEED_LONG_DATA_LEN:
case SQL_ROW_UPDATES:
case SQL_ACCESSIBLE_PROCEDURES:
case SQL_PROCEDURES:
case SQL_EXPRESSIONS_IN_ORDERBY:
case SQL_ODBC_SQL_OPT_IEF:
case SQL_LIKE_ESCAPE_CLAUSE:
case SQL_ORDER_BY_COLUMNS_IN_SELECT:
case SQL_OUTER_JOINS:
case SQL_COLUMN_ALIAS:
case SQL_ACCESSIBLE_TABLES:
case SQL_MULT_RESULT_SETS:
case SQL_MULTIPLE_ACTIVE_TXN:
case SQL_MAX_ROW_SIZE_INCLUDES_LONG:
case SQL_DATA_SOURCE_READ_ONLY:
#ifdef SQL_DESCRIBE_PARAMETER
case SQL_DESCRIBE_PARAMETER:
#endif
case SQL_IDENTIFIER_QUOTE_CHAR:
case SQL_OWNER_TERM:
case SQL_PROCEDURE_TERM:
case SQL_QUALIFIER_NAME_SEPARATOR:
case SQL_QUALIFIER_TERM:
case SQL_TABLE_TERM:
case SQL_KEYWORDS:
case SQL_SPECIAL_CHARACTERS:
#ifdef SQL_CATALOG_NAME
case SQL_CATALOG_NAME:
#endif
#ifdef SQL_COLLATION_SEQ
case SQL_COLLATION_SEQ:
#endif
if (val) {
if (len > 0) {
v = uc_from_utf((SQLCHAR *) val, len);
if (v) {
int vmax = valMax / sizeof (SQLWCHAR);
uc_strncpy(val, v, vmax);
v[len] = 0;
len = min(vmax, uc_strlen(v));
uc_free(v);
len *= sizeof (SQLWCHAR);
} else {
len = 0;
}
}
if (len <= 0) {
len = 0;
if (valMax >= sizeof (SQLWCHAR)) {
*((SQLWCHAR *)val) = 0;
}
}
} else {
len = 0;
}
break;
}
if (valLen) {
*valLen = len;
}
}
return ret;
}
#endif
/**
* Return information about supported ODBC API functions.
* @param dbc database connection handle
* @param func function code to be retrieved
* @param flags output indicator
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetFunctions(SQLHDBC dbc, SQLUSMALLINT func,
SQLUSMALLINT *flags)
{
DBC *d;
int i;
SQLUSMALLINT exists[100];
if (dbc == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
d = (DBC *) dbc;
for (i = 0; i < array_size(exists); i++) {
exists[i] = SQL_FALSE;
}
exists[SQL_API_SQLALLOCCONNECT] = SQL_TRUE;
exists[SQL_API_SQLFETCH] = SQL_TRUE;
exists[SQL_API_SQLALLOCENV] = SQL_TRUE;
exists[SQL_API_SQLFREECONNECT] = SQL_TRUE;
exists[SQL_API_SQLALLOCSTMT] = SQL_TRUE;
exists[SQL_API_SQLFREEENV] = SQL_TRUE;
exists[SQL_API_SQLBINDCOL] = SQL_TRUE;
exists[SQL_API_SQLFREESTMT] = SQL_TRUE;
exists[SQL_API_SQLCANCEL] = SQL_TRUE;
exists[SQL_API_SQLGETCURSORNAME] = SQL_TRUE;
exists[SQL_API_SQLCOLATTRIBUTES] = SQL_TRUE;
exists[SQL_API_SQLNUMRESULTCOLS] = SQL_TRUE;
exists[SQL_API_SQLCONNECT] = SQL_TRUE;
exists[SQL_API_SQLPREPARE] = SQL_TRUE;
exists[SQL_API_SQLDESCRIBECOL] = SQL_TRUE;
exists[SQL_API_SQLROWCOUNT] = SQL_TRUE;
exists[SQL_API_SQLDISCONNECT] = SQL_TRUE;
exists[SQL_API_SQLSETCURSORNAME] = SQL_TRUE;
exists[SQL_API_SQLERROR] = SQL_TRUE;
exists[SQL_API_SQLSETPARAM] = SQL_TRUE;
exists[SQL_API_SQLEXECDIRECT] = SQL_TRUE;
exists[SQL_API_SQLTRANSACT] = SQL_TRUE;
exists[SQL_API_SQLEXECUTE] = SQL_TRUE;
exists[SQL_API_SQLBINDPARAMETER] = SQL_TRUE;
exists[SQL_API_SQLGETTYPEINFO] = SQL_TRUE;
exists[SQL_API_SQLCOLUMNS] = SQL_TRUE;
exists[SQL_API_SQLPARAMDATA] = SQL_TRUE;
exists[SQL_API_SQLDRIVERCONNECT] = SQL_TRUE;
exists[SQL_API_SQLPUTDATA] = SQL_TRUE;
exists[SQL_API_SQLGETCONNECTOPTION] = SQL_TRUE;
exists[SQL_API_SQLSETCONNECTOPTION] = SQL_TRUE;
exists[SQL_API_SQLGETDATA] = SQL_TRUE;
exists[SQL_API_SQLSETSTMTOPTION] = SQL_TRUE;
exists[SQL_API_SQLGETFUNCTIONS] = SQL_TRUE;
exists[SQL_API_SQLSPECIALCOLUMNS] = SQL_TRUE;
exists[SQL_API_SQLGETINFO] = SQL_TRUE;
exists[SQL_API_SQLSTATISTICS] = SQL_TRUE;
exists[SQL_API_SQLGETSTMTOPTION] = SQL_TRUE;
exists[SQL_API_SQLTABLES] = SQL_TRUE;
exists[SQL_API_SQLBROWSECONNECT] = SQL_FALSE;
exists[SQL_API_SQLNUMPARAMS] = SQL_TRUE;
exists[SQL_API_SQLCOLUMNPRIVILEGES] = SQL_FALSE;
exists[SQL_API_SQLPARAMOPTIONS] = SQL_FALSE;
exists[SQL_API_SQLDATASOURCES] = SQL_TRUE;
exists[SQL_API_SQLPRIMARYKEYS] = SQL_TRUE;
exists[SQL_API_SQLDESCRIBEPARAM] = SQL_TRUE;
exists[SQL_API_SQLPROCEDURECOLUMNS] = SQL_TRUE;
exists[SQL_API_SQLDRIVERS] = SQL_FALSE;
exists[SQL_API_SQLPROCEDURES] = SQL_TRUE;
exists[SQL_API_SQLEXTENDEDFETCH] = SQL_TRUE;
exists[SQL_API_SQLSETPOS] = SQL_TRUE;
exists[SQL_API_SQLFOREIGNKEYS] = SQL_TRUE;
exists[SQL_API_SQLSETSCROLLOPTIONS] = SQL_TRUE;
exists[SQL_API_SQLMORERESULTS] = SQL_TRUE;
exists[SQL_API_SQLTABLEPRIVILEGES] = SQL_FALSE;
exists[SQL_API_SQLNATIVESQL] = SQL_TRUE;
if (func == SQL_API_ALL_FUNCTIONS) {
memcpy(flags, exists, sizeof (exists));
} else if (func == SQL_API_ODBC3_ALL_FUNCTIONS) {
int i;
#define SET_EXISTS(x) \
flags[(x) >> 4] |= (1 << ((x) & 0xF))
#define CLR_EXISTS(x) \
flags[(x) >> 4] &= ~(1 << ((x) & 0xF))
memset(flags, 0,
sizeof (SQLUSMALLINT) * SQL_API_ODBC3_ALL_FUNCTIONS_SIZE);
for (i = 0; i < array_size(exists); i++) {
if (exists[i]) {
flags[i >> 4] |= (1 << (i & 0xF));
}
}
SET_EXISTS(SQL_API_SQLALLOCHANDLE);
SET_EXISTS(SQL_API_SQLFREEHANDLE);
SET_EXISTS(SQL_API_SQLGETSTMTATTR);
SET_EXISTS(SQL_API_SQLSETSTMTATTR);
SET_EXISTS(SQL_API_SQLGETCONNECTATTR);
SET_EXISTS(SQL_API_SQLSETCONNECTATTR);
SET_EXISTS(SQL_API_SQLGETENVATTR);
SET_EXISTS(SQL_API_SQLSETENVATTR);
SET_EXISTS(SQL_API_SQLCLOSECURSOR);
SET_EXISTS(SQL_API_SQLBINDPARAM);
#if !defined(HAVE_UNIXODBC) || !HAVE_UNIXODBC
/*
* Some unixODBC versions have problems with
* SQLError() vs. SQLGetDiagRec() with loss
* of error/warning messages.
*/
SET_EXISTS(SQL_API_SQLGETDIAGREC);
#endif
SET_EXISTS(SQL_API_SQLFETCHSCROLL);
SET_EXISTS(SQL_API_SQLENDTRAN);
} else {
if (func < array_size(exists)) {
*flags = exists[func];
} else {
switch (func) {
case SQL_API_SQLALLOCHANDLE:
case SQL_API_SQLFREEHANDLE:
case SQL_API_SQLGETSTMTATTR:
case SQL_API_SQLSETSTMTATTR:
case SQL_API_SQLGETCONNECTATTR:
case SQL_API_SQLSETCONNECTATTR:
case SQL_API_SQLGETENVATTR:
case SQL_API_SQLSETENVATTR:
case SQL_API_SQLCLOSECURSOR:
case SQL_API_SQLBINDPARAM:
#if !defined(HAVE_UNIXODBC) || !HAVE_UNIXODBC
/*
* Some unixODBC versions have problems with
* SQLError() vs. SQLGetDiagRec() with loss
* of error/warning messages.
*/
case SQL_API_SQLGETDIAGREC:
#endif
case SQL_API_SQLFETCHSCROLL:
case SQL_API_SQLENDTRAN:
*flags = SQL_TRUE;
break;
default:
*flags = SQL_FALSE;
}
}
}
return SQL_SUCCESS;
}
/**
* Internal allocate HENV.
* @param env pointer to environment handle
* @result ODBC error code
*/
static SQLRETURN
drvallocenv(SQLHENV *env)
{
ENV *e;
if (env == NULL) {
return SQL_INVALID_HANDLE;
}
e = (ENV *) xmalloc(sizeof (ENV));
if (e == NULL) {
*env = SQL_NULL_HENV;
return SQL_ERROR;
}
e->magic = ENV_MAGIC;
e->ov3 = 0;
#if defined(_WIN32) || defined(_WIN64)
InitializeCriticalSection(&e->cs);
e->owner = 0;
#endif
e->dbcs = NULL;
*env = (SQLHENV) e;
return SQL_SUCCESS;
}
/**
* Allocate HENV.
* @param env pointer to environment handle
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLAllocEnv(SQLHENV *env)
{
return drvallocenv(env);
}
/**
* Internal free HENV.
* @param env environment handle
* @result ODBC error code
*/
static SQLRETURN
drvfreeenv(SQLHENV env)
{
ENV *e;
if (env == SQL_NULL_HENV) {
return SQL_INVALID_HANDLE;
}
e = (ENV *) env;
if (e->magic != ENV_MAGIC) {
return SQL_SUCCESS;
}
#if defined(_WIN32) || defined(_WIN64)
EnterCriticalSection(&e->cs);
e->owner = GetCurrentThreadId();
#endif
if (e->dbcs) {
#if defined(_WIN32) || defined(_WIN64)
e->owner = 0;
LeaveCriticalSection(&e->cs);
#endif
return SQL_ERROR;
}
e->magic = DEAD_MAGIC;
#if defined(_WIN32) || defined(_WIN64)
e->owner = 0;
LeaveCriticalSection(&e->cs);
DeleteCriticalSection(&e->cs);
#endif
xfree(e);
return SQL_SUCCESS;
}
/**
* Free HENV.
* @param env environment handle
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLFreeEnv(SQLHENV env)
{
return drvfreeenv(env);
}
/**
* Internal allocate HDBC.
* @param env environment handle
* @param dbc pointer to database connection handle
* @result ODBC error code
*/
static SQLRETURN
drvallocconnect(SQLHENV env, SQLHDBC *dbc)
{
DBC *d;
ENV *e;
const char *verstr;
int maj = 0, min = 0, lev = 0;
if (dbc == NULL) {
return SQL_ERROR;
}
d = (DBC *) xmalloc(sizeof (DBC));
if (d == NULL) {
*dbc = SQL_NULL_HDBC;
return SQL_ERROR;
}
memset(d, 0, sizeof (DBC));
d->curtype = SQL_CURSOR_STATIC;
#if HAVE_LIBVERSION
verstr = sqlite_libversion();
#else
verstr = sqlite_version;
#endif
sscanf(verstr, "%d.%d.%d", &maj, &min, &lev);
d->version = verinfo(maj & 0xFF, min & 0xFF, lev & 0xFF);
if (d->version < verinfo(2, 8, 0)) {
xfree(d);
return SQL_ERROR;
}
d->ov3 = &d->ov3val;
e = (ENV *) env;
#if defined(_WIN32) || defined(_WIN64)
if (e->magic == ENV_MAGIC) {
EnterCriticalSection(&e->cs);
e->owner = GetCurrentThreadId();
}
#endif
if (e->magic == ENV_MAGIC) {
DBC *n, *p;
d->env = e;
d->ov3 = &e->ov3;
p = NULL;
n = e->dbcs;
while (n) {
p = n;
n = n->next;
}
if (p) {
p->next = d;
} else {
e->dbcs = d;
}
}
#if defined(_WIN32) || defined(_WIN64)
if (e->magic == ENV_MAGIC) {
e->owner = 0;
LeaveCriticalSection(&e->cs);
}
#endif
d->autocommit = 1;
d->magic = DBC_MAGIC;
*dbc = (SQLHDBC) d;
drvgetgpps(d);
return SQL_SUCCESS;
}
/**
* Allocate HDBC.
* @param env environment handle
* @param dbc pointer to database connection handle
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLAllocConnect(SQLHENV env, SQLHDBC *dbc)
{
return drvallocconnect(env, dbc);
}
/**
* Internal free connection (HDBC).
* @param dbc database connection handle
* @result ODBC error code
*/
static SQLRETURN
drvfreeconnect(SQLHDBC dbc)
{
DBC *d;
ENV *e;
SQLRETURN ret = SQL_ERROR;
if (dbc == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
d = (DBC *) dbc;
if (d->magic != DBC_MAGIC) {
return SQL_INVALID_HANDLE;
}
e = d->env;
if (e && e->magic == ENV_MAGIC) {
#if defined(_WIN32) || defined(_WIN64)
EnterCriticalSection(&e->cs);
e->owner = GetCurrentThreadId();
#endif
} else {
e = NULL;
}
if (d->sqlite) {
setstatd(d, -1, "not disconnected", (*d->ov3) ? "HY000" : "S1000");
goto done;
}
while (d->stmt) {
freestmt((HSTMT) d->stmt);
}
if (e && e->magic == ENV_MAGIC) {
DBC *n, *p;
p = NULL;
n = e->dbcs;
while (n) {
if (n == d) {
break;
}
p = n;
n = n->next;
}
if (n) {
if (p) {
p->next = d->next;
} else {
e->dbcs = d->next;
}
}
}
drvrelgpps(d);
d->magic = DEAD_MAGIC;
#if defined(HAVE_SQLITETRACE) && HAVE_SQLITETRACE
if (d->trace) {
fclose(d->trace);
}
#endif
xfree(d);
ret = SQL_SUCCESS;
done:
#if defined(_WIN32) || defined(_WIN64)
if (e) {
e->owner = 0;
LeaveCriticalSection(&e->cs);
}
#endif
return ret;
}
/**
* Free connection (HDBC).
* @param dbc database connection handle
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLFreeConnect(SQLHDBC dbc)
{
return drvfreeconnect(dbc);
}
/**
* Internal get connect attribute of HDBC.
* @param dbc database connection handle
* @param attr option to be retrieved
* @param val output buffer
* @param bufmax size of output buffer
* @param buflen output length
* @result ODBC error code
*/
static SQLRETURN
drvgetconnectattr(SQLHDBC dbc, SQLINTEGER attr, SQLPOINTER val,
SQLINTEGER bufmax, SQLINTEGER *buflen)
{
DBC *d;
SQLINTEGER dummy;
if (dbc == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
d = (DBC *) dbc;
if (!val) {
val = (SQLPOINTER) &dummy;
}
if (!buflen) {
buflen = &dummy;
}
switch (attr) {
case SQL_ATTR_CONNECTION_DEAD:
*((SQLINTEGER *) val) = d->sqlite ? SQL_CD_FALSE : SQL_CD_TRUE;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_ACCESS_MODE:
*((SQLINTEGER *) val) = SQL_MODE_READ_WRITE;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_AUTOCOMMIT:
*((SQLINTEGER *) val) =
d->autocommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_LOGIN_TIMEOUT:
*((SQLINTEGER *) val) = 100;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_ODBC_CURSORS:
*((SQLINTEGER *) val) = SQL_CUR_USE_DRIVER;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_PACKET_SIZE:
*((SQLINTEGER *) val) = 16384;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_TXN_ISOLATION:
*((SQLINTEGER *) val) = SQL_TXN_SERIALIZABLE;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_TRACEFILE:
case SQL_ATTR_CURRENT_CATALOG:
case SQL_ATTR_TRANSLATE_LIB:
*((SQLCHAR *) val) = 0;
*buflen = 0;
break;
case SQL_ATTR_TRACE:
case SQL_ATTR_QUIET_MODE:
case SQL_ATTR_TRANSLATE_OPTION:
case SQL_ATTR_KEYSET_SIZE:
case SQL_ATTR_QUERY_TIMEOUT:
*((SQLINTEGER *) val) = 0;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_PARAM_BIND_TYPE:
*((SQLUINTEGER *) val) = SQL_PARAM_BIND_BY_COLUMN;
*buflen = sizeof (SQLUINTEGER);
break;
case SQL_ATTR_ROW_BIND_TYPE:
*((SQLUINTEGER *) val) = SQL_BIND_BY_COLUMN;
*buflen = sizeof (SQLUINTEGER);
break;
case SQL_ATTR_USE_BOOKMARKS:
*((SQLINTEGER *) val) = SQL_UB_OFF;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_ASYNC_ENABLE:
*((SQLINTEGER *) val) = SQL_ASYNC_ENABLE_OFF;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_NOSCAN:
*((SQLINTEGER *) val) = SQL_NOSCAN_OFF;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_CONCURRENCY:
*((SQLINTEGER *) val) = SQL_CONCUR_LOCK;
*buflen = sizeof (SQLINTEGER);
break;
#ifdef SQL_ATTR_CURSOR_SENSITIVITY
case SQL_ATTR_CURSOR_SENSITIVITY:
*((SQLINTEGER *) val) = SQL_UNSPECIFIED;
*buflen = sizeof (SQLINTEGER);
break;
#endif
case SQL_ATTR_SIMULATE_CURSOR:
*((SQLINTEGER *) val) = SQL_SC_NON_UNIQUE;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_MAX_ROWS:
case SQL_ATTR_MAX_LENGTH:
*((SQLINTEGER *) val) = 1000000000;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_CURSOR_TYPE:
*((SQLINTEGER *) val) = d->curtype;
*buflen = sizeof (SQLINTEGER);
break;
case SQL_ATTR_RETRIEVE_DATA:
*((SQLINTEGER *) val) = SQL_RD_ON;
*buflen = sizeof (SQLINTEGER);
break;
default:
setstatd(d, -1, "unsupported connect attribute %d",
(*d->ov3) ? "HYC00" : "S1C00", (int) attr);
return SQL_ERROR;
}
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Get connect attribute of HDBC.
* @param dbc database connection handle
* @param attr option to be retrieved
* @param val output buffer
* @param bufmax size of output buffer
* @param buflen output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetConnectAttr(SQLHDBC dbc, SQLINTEGER attr, SQLPOINTER val,
SQLINTEGER bufmax, SQLINTEGER *buflen)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvgetconnectattr(dbc, attr, val, bufmax, buflen);
HDBC_UNLOCK(dbc);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Get connect attribute of HDBC (UNICODE version).
* @param dbc database connection handle
* @param attr option to be retrieved
* @param val output buffer
* @param bufmax size of output buffer
* @param buflen output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetConnectAttrW(SQLHDBC dbc, SQLINTEGER attr, SQLPOINTER val,
SQLINTEGER bufmax, SQLINTEGER *buflen)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvgetconnectattr(dbc, attr, val, bufmax, buflen);
if (SQL_SUCCEEDED(ret)) {
switch (attr) {
case SQL_ATTR_TRACEFILE:
case SQL_ATTR_CURRENT_CATALOG:
case SQL_ATTR_TRANSLATE_LIB:
if (val && bufmax >= sizeof (SQLWCHAR)) {
*(SQLWCHAR *) val = 0;
}
break;
}
}
HDBC_UNLOCK(dbc);
return ret;
}
#endif
/**
* Internal set connect attribute of HDBC.
* @param dbc database connection handle
* @param attr option to be set
* @param val option value
* @param len size of option
* @result ODBC error code
*/
static SQLRETURN
drvsetconnectattr(SQLHDBC dbc, SQLINTEGER attr, SQLPOINTER val,
SQLINTEGER len)
{
DBC *d;
if (dbc == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
d = (DBC *) dbc;
switch (attr) {
case SQL_AUTOCOMMIT:
d->autocommit = val == (SQLPOINTER) SQL_AUTOCOMMIT_ON;
if (d->autocommit && d->intrans) {
return endtran(d, SQL_COMMIT, 1);
} else if (!d->autocommit) {
vm_end(d->vm_stmt);
}
return SQL_SUCCESS;
default:
setstatd(d, -1, "option value changed", "01S02");
return SQL_SUCCESS_WITH_INFO;
}
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Set connect attribute of HDBC.
* @param dbc database connection handle
* @param attr option to be set
* @param val option value
* @param len size of option
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSetConnectAttr(SQLHDBC dbc, SQLINTEGER attr, SQLPOINTER val,
SQLINTEGER len)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvsetconnectattr(dbc, attr, val, len);
HDBC_UNLOCK(dbc);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Set connect attribute of HDBC (UNICODE version).
* @param dbc database connection handle
* @param attr option to be set
* @param val option value
* @param len size of option
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSetConnectAttrW(SQLHDBC dbc, SQLINTEGER attr, SQLPOINTER val,
SQLINTEGER len)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvsetconnectattr(dbc, attr, val, len);
HDBC_UNLOCK(dbc);
return ret;
}
#endif
/**
* Internal get connect option of HDBC.
* @param dbc database connection handle
* @param opt option to be retrieved
* @param param output buffer
* @result ODBC error code
*/
static SQLRETURN
drvgetconnectoption(SQLHDBC dbc, SQLUSMALLINT opt, SQLPOINTER param)
{
DBC *d;
SQLINTEGER dummy;
if (dbc == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
d = (DBC *) dbc;
if (!param) {
param = (SQLPOINTER) &dummy;
}
switch (opt) {
case SQL_ACCESS_MODE:
*((SQLINTEGER *) param) = SQL_MODE_READ_WRITE;
break;
case SQL_AUTOCOMMIT:
*((SQLINTEGER *) param) =
d->autocommit ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF;
break;
case SQL_LOGIN_TIMEOUT:
*((SQLINTEGER *) param) = 100;
break;
case SQL_ODBC_CURSORS:
*((SQLINTEGER *) param) = SQL_CUR_USE_DRIVER;
break;
case SQL_PACKET_SIZE:
*((SQLINTEGER *) param) = 16384;
break;
case SQL_TXN_ISOLATION:
*((SQLINTEGER *) param) = SQL_TXN_SERIALIZABLE;
break;
case SQL_OPT_TRACEFILE:
case SQL_CURRENT_QUALIFIER:
case SQL_TRANSLATE_DLL:
*((SQLCHAR *) param) = 0;
break;
case SQL_OPT_TRACE:
case SQL_QUIET_MODE:
case SQL_KEYSET_SIZE:
case SQL_QUERY_TIMEOUT:
case SQL_BIND_TYPE:
case SQL_TRANSLATE_OPTION:
*((SQLINTEGER *) param) = 0;
break;
case SQL_USE_BOOKMARKS:
*((SQLINTEGER *) param) = SQL_UB_OFF;
break;
case SQL_ASYNC_ENABLE:
*((SQLINTEGER *) param) = SQL_ASYNC_ENABLE_OFF;
break;
case SQL_NOSCAN:
*((SQLINTEGER *) param) = SQL_NOSCAN_OFF;
break;
case SQL_CONCURRENCY:
*((SQLINTEGER *) param) = SQL_CONCUR_LOCK;
break;
case SQL_SIMULATE_CURSOR:
*((SQLINTEGER *) param) = SQL_SC_NON_UNIQUE;
break;
case SQL_ROWSET_SIZE:
case SQL_MAX_ROWS:
case SQL_MAX_LENGTH:
*((SQLINTEGER *) param) = 1000000000;
break;
case SQL_CURSOR_TYPE:
*((SQLINTEGER *) param) = d->curtype;
break;
case SQL_RETRIEVE_DATA:
*((SQLINTEGER *) param) = SQL_RD_ON;
break;
default:
setstatd(d, -1, "unsupported connect option %d",
(*d->ov3) ? "HYC00" : "S1C00", opt);
return SQL_ERROR;
}
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Get connect option of HDBC.
* @param dbc database connection handle
* @param opt option to be retrieved
* @param param output buffer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetConnectOption(SQLHDBC dbc, SQLUSMALLINT opt, SQLPOINTER param)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvgetconnectoption(dbc, opt, param);
HDBC_UNLOCK(dbc);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Get connect option of HDBC (UNICODE version).
* @param dbc database connection handle
* @param opt option to be retrieved
* @param param output buffer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetConnectOptionW(SQLHDBC dbc, SQLUSMALLINT opt, SQLPOINTER param)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvgetconnectoption(dbc, opt, param);
if (SQL_SUCCEEDED(ret)) {
switch (opt) {
case SQL_OPT_TRACEFILE:
case SQL_CURRENT_QUALIFIER:
case SQL_TRANSLATE_DLL:
if (param) {
*(SQLWCHAR *) param = 0;
}
break;
}
}
HDBC_UNLOCK(dbc);
return ret;
}
#endif
/**
* Internal set option on HDBC.
* @param dbc database connection handle
* @param opt option to be set
* @param param option value
* @result ODBC error code
*/
static SQLRETURN
drvsetconnectoption(SQLHDBC dbc, SQLUSMALLINT opt, SQLUINTEGER param)
{
DBC *d;
if (dbc == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
d = (DBC *) dbc;
switch (opt) {
case SQL_AUTOCOMMIT:
d->autocommit = param == SQL_AUTOCOMMIT_ON;
if (d->autocommit && d->intrans) {
return endtran(d, SQL_COMMIT, 1);
} else if (!d->autocommit) {
vm_end(d->vm_stmt);
}
break;
default:
setstatd(d, -1, "option value changed", "01S02");
return SQL_SUCCESS_WITH_INFO;
}
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Set option on HDBC.
* @param dbc database connection handle
* @param opt option to be set
* @param param option value
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSetConnectOption(SQLHDBC dbc, SQLUSMALLINT opt, SQLULEN param)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvsetconnectoption(dbc, opt, param);
HDBC_UNLOCK(dbc);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Set option on HDBC (UNICODE version).
* @param dbc database connection handle
* @param opt option to be set
* @param param option value
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSetConnectOptionW(SQLHDBC dbc, SQLUSMALLINT opt, SQLULEN param)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvsetconnectoption(dbc, opt, param);
HDBC_UNLOCK(dbc);
return ret;
}
#endif
#if defined(WITHOUT_DRIVERMGR) || (!defined(_WIN32) && !defined(_WIN64) && !defined(__OS2__))
/**
* Handling of SQLConnect() connection attributes
* for standalone operation without driver manager.
* @param dsn DSN/driver connection string
* @param attr attribute string to be retrieved
* @param out output buffer
* @param outLen length of output buffer
* @result true or false
*/
static int
getdsnattr(char *dsn, char *attr, char *out, int outLen)
{
char *str = dsn, *start;
int len = strlen(attr);
while (*str) {
while (*str && *str == ';') {
++str;
}
start = str;
if ((str = strchr(str, '=')) == NULL) {
return 0;
}
if (str - start == len && strncasecmp(start, attr, len) == 0) {
start = ++str;
while (*str && *str != ';') {
++str;
}
len = min(outLen - 1, str - start);
strncpy(out, start, len);
out[len] = '\0';
return 1;
}
while (*str && *str != ';') {
++str;
}
}
return 0;
}
#endif
/**
* Internal connect to SQLite database.
* @param dbc database connection handle
* @param dsn DSN string
* @param dsnLen length of DSN string or SQL_NTS
* @result ODBC error code
*/
static SQLRETURN
drvconnect(SQLHDBC dbc, SQLCHAR *dsn, SQLSMALLINT dsnLen)
{
DBC *d;
int len;
char buf[SQL_MAX_MESSAGE_LENGTH], dbname[SQL_MAX_MESSAGE_LENGTH / 4];
char busy[SQL_MAX_MESSAGE_LENGTH / 4];
char sflag[32], ntflag[32], nwflag[32], lnflag[32];
#if defined(HAVE_SQLITETRACE) && HAVE_SQLITETRACE
char tracef[SQL_MAX_MESSAGE_LENGTH];
#endif
if (dbc == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
d = (DBC *) dbc;
if (d->magic != DBC_MAGIC) {
return SQL_INVALID_HANDLE;
}
if (d->sqlite != NULL) {
setstatd(d, -1, "connection already established", "08002");
return SQL_ERROR;
}
buf[0] = '\0';
if (dsnLen == SQL_NTS) {
len = sizeof (buf) - 1;
} else {
len = min(sizeof (buf) - 1, dsnLen);
}
if (dsn != NULL) {
strncpy(buf, (char *) dsn, len);
}
buf[len] = '\0';
if (buf[0] == '\0') {
setstatd(d, -1, "invalid DSN", (*d->ov3) ? "HY090" : "S1090");
return SQL_ERROR;
}
busy[0] = '\0';
dbname[0] = '\0';
#ifdef WITHOUT_DRIVERMGR
getdsnattr(buf, "database", dbname, sizeof (dbname));
if (dbname[0] == '\0') {
strncpy(dbname, buf, sizeof (dbname));
dbname[sizeof (dbname) - 1] = '\0';
}
getdsnattr(buf, "timeout", busy, sizeof (busy));
sflag[0] = '\0';
ntflag[0] = '\0';
nwflag[0] = '\0';
lnflag[0] = '\0';
getdsnattr(buf, "stepapi", sflag, sizeof (sflag));
getdsnattr(buf, "notxn", ntflag, sizeof (ntflag));
getdsnattr(buf, "nowchar", nwflag, sizeof (nwflag));
getdsnattr(buf, "longnames", lnflag, sizeof (lnflag));
#else
SQLGetPrivateProfileString(buf, "timeout", "100000",
busy, sizeof (busy), ODBC_INI);
SQLGetPrivateProfileString(buf, "database", "",
dbname, sizeof (dbname), ODBC_INI);
SQLGetPrivateProfileString(buf, "stepapi", "",
sflag, sizeof (sflag), ODBC_INI);
SQLGetPrivateProfileString(buf, "notxn", "",
ntflag, sizeof (ntflag), ODBC_INI);
SQLGetPrivateProfileString(buf, "nowchar", "",
nwflag, sizeof (nwflag), ODBC_INI);
SQLGetPrivateProfileString(buf, "longnames", "",
lnflag, sizeof (lnflag), ODBC_INI);
#endif
#if defined(HAVE_SQLITETRACE) && HAVE_SQLITETRACE
tracef[0] = '\0';
#ifdef WITHOUT_DRIVERMGR
getdsnattr(buf, "tracefile", tracef, sizeof (tracef));
#else
SQLGetPrivateProfileString(buf, "tracefile", "",
tracef, sizeof (tracef), ODBC_INI);
#endif
if (tracef[0] != '\0') {
d->trace = fopen(tracef, "a");
}
#endif
d->nowchar = getbool(nwflag);
d->longnames = getbool(lnflag);
return dbopen(d, dbname, (char *) dsn, sflag, ntflag, busy);
}
#ifndef WINTERFACE
/**
* Connect to SQLite database.
* @param dbc database connection handle
* @param dsn DSN string
* @param dsnLen length of DSN string or SQL_NTS
* @param uid user id string or NULL
* @param uidLen length of user id string or SQL_NTS
* @param pass password string or NULL
* @param passLen length of password string or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLConnect(SQLHDBC dbc, SQLCHAR *dsn, SQLSMALLINT dsnLen,
SQLCHAR *uid, SQLSMALLINT uidLen,
SQLCHAR *pass, SQLSMALLINT passLen)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvconnect(dbc, dsn, dsnLen);
HDBC_UNLOCK(dbc);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Connect to SQLite database.
* @param dbc database connection handle
* @param dsn DSN string
* @param dsnLen length of DSN string or SQL_NTS
* @param uid user id string or NULL
* @param uidLen length of user id string or SQL_NTS
* @param pass password string or NULL
* @param passLen length of password string or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLConnectW(SQLHDBC dbc, SQLWCHAR *dsn, SQLSMALLINT dsnLen,
SQLWCHAR *uid, SQLSMALLINT uidLen,
SQLWCHAR *pass, SQLSMALLINT passLen)
{
char *dsna = NULL;
SQLRETURN ret;
HDBC_LOCK(dbc);
if (dsn) {
dsna = uc_to_utf_c(dsn, dsnLen);
if (!dsna) {
DBC *d = (DBC *) dbc;
setstatd(d, -1, "out of memory", (*d->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
}
ret = drvconnect(dbc, (SQLCHAR *) dsna, SQL_NTS);
HDBC_UNLOCK(dbc);
uc_free(dsna);
return ret;
}
#endif
/**
* Internal disconnect given HDBC.
* @param dbc database connection handle
* @result ODBC error code
*/
static SQLRETURN
drvdisconnect(SQLHDBC dbc)
{
DBC *d;
if (dbc == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
d = (DBC *) dbc;
if (d->magic != DBC_MAGIC) {
return SQL_INVALID_HANDLE;
}
if (d->intrans) {
setstatd(d, -1, "incomplete transaction", "25000");
return SQL_ERROR;
}
if (d->vm_stmt) {
vm_end(d->vm_stmt);
}
if (d->sqlite) {
sqlite_close(d->sqlite);
d->sqlite = NULL;
}
freep(&d->dbname);
freep(&d->dsn);
return SQL_SUCCESS;
}
/**
* Disconnect given HDBC.
* @param dbc database connection handle
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLDisconnect(SQLHDBC dbc)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvdisconnect(dbc);
HDBC_UNLOCK(dbc);
return ret;
}
#if defined(WITHOUT_DRIVERMGR) || (!defined(_WIN32) && !defined(_WIN64) && !defined(__OS2__))
/**
* Internal standalone (w/o driver manager) database connect.
* @param dbc database connection handle
* @param hwnd dummy window handle or NULL
* @param connIn driver connect input string
* @param connInLen length of driver connect input string or SQL_NTS
* @param connOut driver connect output string
* @param connOutMax length of driver connect output string
* @param connOutLen output length of driver connect output string
* @param drvcompl completion type
* @result ODBC error code
*/
static SQLRETURN
drvdriverconnect(SQLHDBC dbc, SQLHWND hwnd,
SQLCHAR *connIn, SQLSMALLINT connInLen,
SQLCHAR *connOut, SQLSMALLINT connOutMax,
SQLSMALLINT *connOutLen, SQLUSMALLINT drvcompl)
{
DBC *d;
int len;
char buf[SQL_MAX_MESSAGE_LENGTH], dbname[SQL_MAX_MESSAGE_LENGTH / 4];
char dsn[SQL_MAX_MESSAGE_LENGTH / 4], busy[SQL_MAX_MESSAGE_LENGTH / 4];
char sflag[32], ntflag[32], lnflag[32];
#if defined(HAVE_SQLITETRACE) && HAVE_SQLITETRACE
char tracef[SQL_MAX_MESSAGE_LENGTH];
#endif
if (dbc == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
if (drvcompl != SQL_DRIVER_COMPLETE &&
drvcompl != SQL_DRIVER_COMPLETE_REQUIRED &&
drvcompl != SQL_DRIVER_PROMPT &&
drvcompl != SQL_DRIVER_NOPROMPT) {
return SQL_NO_DATA;
}
d = (DBC *) dbc;
if (d->sqlite) {
setstatd(d, -1, "connection already established", "08002");
return SQL_ERROR;
}
buf[0] = '\0';
if (connInLen == SQL_NTS) {
len = sizeof (buf) - 1;
} else {
len = min(connInLen, sizeof (buf) - 1);
}
if (connIn != NULL) {
strncpy(buf, (char *) connIn, len);
}
buf[len] = '\0';
if (!buf[0]) {
setstatd(d, -1, "invalid connect attributes",
(*d->ov3) ? "HY090" : "S1090");
return SQL_ERROR;
}
dsn[0] = '\0';
getdsnattr(buf, "DSN", dsn, sizeof (dsn));
/* special case: connIn is sole DSN value without keywords */
if (!dsn[0] && !strchr(buf, ';') && !strchr(buf, '=')) {
strncpy(dsn, buf, sizeof (dsn) - 1);
dsn[sizeof (dsn) - 1] = '\0';
}
busy[0] = '\0';
getdsnattr(buf, "timeout", busy, sizeof (busy));
#ifndef WITHOUT_DRIVERMGR
if (dsn[0] && !busy[0]) {
SQLGetPrivateProfileString(dsn, "timeout", "100000",
busy, sizeof (busy), ODBC_INI);
}
#endif
dbname[0] = '\0';
getdsnattr(buf, "database", dbname, sizeof (dbname));
#ifndef WITHOUT_DRIVERMGR
if (dsn[0] && !dbname[0]) {
SQLGetPrivateProfileString(dsn, "database", "",
dbname, sizeof (dbname), ODBC_INI);
}
#endif
sflag[0] = '\0';
getdsnattr(buf, "stepapi", sflag, sizeof (sflag));
#ifndef WITHOUT_DRIVERMGR
if (dsn[0] && !sflag[0]) {
SQLGetPrivateProfileString(dsn, "stepapi", "",
sflag, sizeof (sflag), ODBC_INI);
}
#endif
ntflag[0] = '\0';
getdsnattr(buf, "notxn", ntflag, sizeof (ntflag));
#ifndef WITHOUT_DRIVERMGR
if (dsn[0] && !ntflag[0]) {
SQLGetPrivateProfileString(dsn, "notxn", "",
ntflag, sizeof (ntflag), ODBC_INI);
}
#endif
lnflag[0] = '\0';
getdsnattr(buf, "longnames", lnflag, sizeof (lnflag));
#ifndef WITHOUT_DRIVERMGR
if (dsn[0] && !lnflag[0]) {
SQLGetPrivateProfileString(dsn, "longnames", "",
lnflag, sizeof (lnflag), ODBC_INI);
}
#endif
if (!dbname[0] && !dsn[0]) {
strcpy(dsn, "SQLite");
strncpy(dbname, buf, sizeof (dbname));
dbname[sizeof (dbname) - 1] = '\0';
}
#if defined(HAVE_SQLITETRACE) && HAVE_SQLITETRACE
tracef[0] = '\0';
getdsnattr(buf, "tracefile", tracef, sizeof (tracef));
#ifndef WITHOUT_DRIVERMGR
if (dsn[0] && !tracef[0]) {
SQLGetPrivateProfileString(dsn, "tracefile", "",
tracef, sizeof (tracef), ODBC_INI);
}
#endif
#endif
if (connOut || connOutLen) {
int count;
buf[0] = '\0';
count = snprintf(buf, sizeof (buf),
#if defined(HAVE_SQLITETRACE) && HAVE_SQLITETRACE
"DSN=%s;Database=%s;StepAPI=%s;NoTXN=%s;"
"Timeout=%s;LongNames=%s;Tracefile=%s",
dsn, dbname, sflag, ntflag, busy, lnflag, tracef
#else
"DSN=%s;Database=%s;StepAPI=%s;NoTXN=%s;"
"Timeout=%s;LongNames=%s",
dsn, dbname, sflag, ntflag, busy, lnflag
#endif
);
if (count < 0) {
buf[sizeof (buf) - 1] = '\0';
}
len = min(connOutMax - 1, strlen(buf));
if (connOut) {
strncpy((char *) connOut, buf, len);
connOut[len] = '\0';
}
if (connOutLen) {
*connOutLen = len;
}
}
#if defined(HAVE_SQLITETRACE) && HAVE_SQLITETRACE
if (tracef[0] != '\0') {
d->trace = fopen(tracef, "a");
}
#endif
d->longnames = getbool(lnflag);
return dbopen(d, dbname, dsn, sflag, ntflag, busy);
}
#endif
/**
* Internal free function for HSTMT.
* @param stmt statement handle
* @result ODBC error code
*/
static SQLRETURN
freestmt(SQLHSTMT stmt)
{
STMT *s;
DBC *d;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
freeresult(s, 1);
freep(&s->query);
d = (DBC *) s->dbc;
if (d && d->magic == DBC_MAGIC) {
STMT *p, *n;
p = NULL;
n = d->stmt;
while (n) {
if (n == s) {
break;
}
p = n;
n = n->next;
}
if (n) {
if (p) {
p->next = s->next;
} else {
d->stmt = s->next;
}
}
}
freeparams(s);
freep(&s->bindparms);
if (s->row_status0 != &s->row_status1) {
freep(&s->row_status0);
s->rowset_size = 1;
s->row_status0 = &s->row_status1;
}
xfree(s);
return SQL_SUCCESS;
}
/**
* Allocate HSTMT given HDBC (driver internal version).
* @param dbc database connection handle
* @param stmt pointer to statement handle
* @result ODBC error code
*/
static SQLRETURN
drvallocstmt(SQLHDBC dbc, SQLHSTMT *stmt)
{
DBC *d;
STMT *s, *sl, *pl;
if (dbc == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
d = (DBC *) dbc;
if (d->magic != DBC_MAGIC || stmt == NULL) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) xmalloc(sizeof (STMT));
if (s == NULL) {
*stmt = SQL_NULL_HSTMT;
return SQL_ERROR;
}
*stmt = (SQLHSTMT) s;
memset(s, 0, sizeof (STMT));
s->dbc = dbc;
s->ov3 = d->ov3;
s->nowchar[0] = d->nowchar;
s->nowchar[1] = 0;
s->longnames = d->longnames;
s->curtype = d->curtype;
s->row_status0 = &s->row_status1;
s->rowset_size = 1;
s->retr_data = SQL_RD_ON;
s->bind_type = SQL_BIND_BY_COLUMN;
s->bind_offs = NULL;
s->paramset_size = 1;
s->parm_bind_type = SQL_PARAM_BIND_BY_COLUMN;
#ifdef _WIN64
sprintf((char *) s->cursorname, "CUR_%08lX", (SQLUBIGINT) *stmt);
#else
sprintf((char *) s->cursorname, "CUR_%08lX", (long) *stmt);
#endif
sl = d->stmt;
pl = NULL;
while (sl) {
pl = sl;
sl = sl->next;
}
if (pl) {
pl->next = s;
} else {
d->stmt = s;
}
return SQL_SUCCESS;
}
/**
* Allocate HSTMT given HDBC.
* @param dbc database connection handle
* @param stmt pointer to statement handle
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLAllocStmt(SQLHDBC dbc, SQLHSTMT *stmt)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvallocstmt(dbc, stmt);
HDBC_UNLOCK(dbc);
return ret;
}
/**
* Internal function to perform certain kinds of free/close on STMT.
* @param stmt statement handle
* @param opt SQL_RESET_PARAMS, SQL_UNBIND, SQL_CLOSE, or SQL_DROP
* @result ODBC error code
*/
static SQLRETURN
drvfreestmt(SQLHSTMT stmt, SQLUSMALLINT opt)
{
STMT *s;
SQLRETURN ret = SQL_SUCCESS;
SQLHDBC dbc;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
HSTMT_LOCK(stmt);
s = (STMT *) stmt;
dbc = s->dbc;
switch (opt) {
case SQL_RESET_PARAMS:
freeparams(s);
break;
case SQL_UNBIND:
unbindcols(s);
break;
case SQL_CLOSE:
vm_end_if(s);
freeresult(s, 0);
break;
case SQL_DROP:
vm_end_if(s);
ret = freestmt(stmt);
break;
default:
setstat(s, -1, "unsupported option", (*s->ov3) ? "HYC00" : "S1C00");
ret = SQL_ERROR;
break;
}
HDBC_UNLOCK(dbc);
return ret;
}
/**
* Free HSTMT.
* @param stmt statement handle
* @param opt SQL_RESET_PARAMS, SQL_UNBIND, SQL_CLOSE, or SQL_DROP
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLFreeStmt(SQLHSTMT stmt, SQLUSMALLINT opt)
{
return drvfreestmt(stmt, opt);
}
/**
* Cancel HSTMT closing cursor.
* @param stmt statement handle
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLCancel(SQLHSTMT stmt)
{
if (stmt != SQL_NULL_HSTMT) {
DBC *d = (DBC *) ((STMT *) stmt)->dbc;
#if defined(_WIN32) || defined(_WIN64)
/* interrupt when other thread owns critical section */
int i;
for (i = 0; i < 2; i++) {
if (d->magic == DBC_MAGIC && d->env &&
d->env->magic == ENV_MAGIC &&
d->env->owner != GetCurrentThreadId() &&
d->env->owner != 0) {
d->busyint = 1;
sqlite_interrupt(d->sqlite);
}
Sleep(1);
}
#else
if (d->magic == DBC_MAGIC) {
d->busyint = 1;
sqlite_interrupt(d->sqlite);
}
#endif
}
return drvfreestmt(stmt, SQL_CLOSE);
}
/**
* Internal function to get cursor name of STMT.
* @param stmt statement handle
* @param cursor output buffer
* @param buflen length of output buffer
* @param lenp output length
* @result ODBC error code
*/
static SQLRETURN
drvgetcursorname(SQLHSTMT stmt, SQLCHAR *cursor, SQLSMALLINT buflen,
SQLSMALLINT *lenp)
{
STMT *s;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (lenp && !cursor) {
*lenp = strlen((char *) s->cursorname);
return SQL_SUCCESS;
}
if (cursor) {
if (buflen > 0) {
strncpy((char *) cursor, (char *) s->cursorname, buflen - 1);
cursor[buflen - 1] = '\0';
}
if (lenp) {
*lenp = min(strlen((char *) s->cursorname), buflen - 1);
}
}
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Get cursor name of STMT.
* @param stmt statement handle
* @param cursor output buffer
* @param buflen length of output buffer
* @param lenp output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetCursorName(SQLHSTMT stmt, SQLCHAR *cursor, SQLSMALLINT buflen,
SQLSMALLINT *lenp)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvgetcursorname(stmt, cursor, buflen, lenp);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Get cursor name of STMT (UNICODE version).
* @param stmt statement handle
* @param cursor output buffer
* @param buflen length of output buffer
* @param lenp output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetCursorNameW(SQLHSTMT stmt, SQLWCHAR *cursor, SQLSMALLINT buflen,
SQLSMALLINT *lenp)
{
SQLRETURN ret;
SQLSMALLINT len = 0;
HSTMT_LOCK(stmt);
ret = drvgetcursorname(stmt, (SQLCHAR *) cursor, buflen, &len);
if (ret == SQL_SUCCESS) {
SQLWCHAR *c = NULL;
if (cursor) {
c = uc_from_utf((SQLCHAR *) cursor, len);
if (!c) {
ret = nomem((STMT *) stmt);
goto done;
}
c[len] = 0;
len = uc_strlen(c);
if (buflen > 0) {
uc_strncpy(cursor, c, buflen - 1);
cursor[buflen - 1] = 0;
}
uc_free(c);
}
if (lenp) {
*lenp = min(len, buflen - 1);
}
}
done:
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
/**
* Internal function to set cursor name on STMT.
* @param stmt statement handle
* @param cursor new cursor name
* @param len length of cursor name or SQL_NTS
* @result ODBC error code
*/
static SQLRETURN
drvsetcursorname(SQLHSTMT stmt, SQLCHAR *cursor, SQLSMALLINT len)
{
STMT *s;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (!cursor ||
!((cursor[0] >= 'A' && cursor[0] <= 'Z') ||
(cursor[0] >= 'a' && cursor[0] <= 'z'))) {
setstat(s, -1, "invalid cursor name", (*s->ov3) ? "HYC00" : "S1C00");
return SQL_ERROR;
}
if (len == SQL_NTS) {
len = sizeof (s->cursorname) - 1;
} else {
len = min(sizeof (s->cursorname) - 1, len);
}
strncpy((char *) s->cursorname, (char *) cursor, len);
s->cursorname[len] = '\0';
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Set cursor name on STMT.
* @param stmt statement handle
* @param cursor new cursor name
* @param len length of cursor name or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSetCursorName(SQLHSTMT stmt, SQLCHAR *cursor, SQLSMALLINT len)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvsetcursorname(stmt, cursor, len);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Set cursor name on STMT (UNICODE version).
* @param stmt statement handle
* @param cursor new cursor name
* @param len length of cursor name or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLSetCursorNameW(SQLHSTMT stmt, SQLWCHAR *cursor, SQLSMALLINT len)
{
char *c = NULL;
SQLRETURN ret;
HSTMT_LOCK(stmt);
if (cursor) {
c = uc_to_utf_c(cursor, len);
if (!c) {
ret = nomem((STMT *) stmt);
goto done;
}
}
ret = drvsetcursorname(stmt, (SQLCHAR *) c, SQL_NTS);
done:
HSTMT_UNLOCK(stmt);
uc_free(c);
return ret;
}
#endif
/**
* Close open cursor.
* @param stmt statement handle
* @return ODBC error code
*/
SQLRETURN SQL_API
SQLCloseCursor(SQLHSTMT stmt)
{
return drvfreestmt(stmt, SQL_CLOSE);
}
/**
* Allocate a HENV, HDBC, or HSTMT handle.
* @param type handle type
* @param input input handle (HENV, HDBC)
* @param output pointer to output handle (HENV, HDBC, HSTMT)
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLAllocHandle(SQLSMALLINT type, SQLHANDLE input, SQLHANDLE *output)
{
SQLRETURN ret;
switch (type) {
case SQL_HANDLE_ENV:
ret = drvallocenv((SQLHENV *) output);
if (ret == SQL_SUCCESS) {
ENV *e = (ENV *) *output;
if (e && e->magic == ENV_MAGIC) {
e->ov3 = 1;
}
}
return ret;
case SQL_HANDLE_DBC:
return drvallocconnect((SQLHENV) input, (SQLHDBC *) output);
case SQL_HANDLE_STMT:
HDBC_LOCK((SQLHDBC) input);
ret = drvallocstmt((SQLHDBC) input, (SQLHSTMT *) output);
HDBC_UNLOCK((SQLHDBC) input);
return ret;
}
return SQL_ERROR;
}
/**
* Free a HENV, HDBC, or HSTMT handle.
* @param type handle type
* @param h handle (HENV, HDBC, or HSTMT)
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLFreeHandle(SQLSMALLINT type, SQLHANDLE h)
{
switch (type) {
case SQL_HANDLE_ENV:
return drvfreeenv((SQLHENV) h);
case SQL_HANDLE_DBC:
return drvfreeconnect((SQLHDBC) h);
case SQL_HANDLE_STMT:
return drvfreestmt((SQLHSTMT) h, SQL_DROP);
}
return SQL_ERROR;
}
/**
* Free dynamically allocated column descriptions of STMT.
* @param s statement pointer
*/
static void
freedyncols(STMT *s)
{
if (s->dyncols) {
int i;
for (i = 0; i < s->dcols; i++) {
freep(&s->dyncols[i].typename);
}
if (s->cols == s->dyncols) {
s->cols = NULL;
s->ncols = 0;
}
freep(&s->dyncols);
}
s->dcols = 0;
}
/**
* Free statement's result.
* @param s statement pointer
* @param clrcols flag to clear column information
*
* The result rows are free'd using the rowfree function pointer.
* If clrcols is greater than zero, then column bindings and dynamic column
* descriptions are free'd.
* If clrcols is less than zero, then dynamic column descriptions are free'd.
*/
static void
freeresult(STMT *s, int clrcols)
{
#if HAVE_ENCDEC
freep(&s->bincache);
freep(&s->hexcache);
s->bincell = NULL;
#endif
if (s->rows) {
if (s->rowfree) {
s->rowfree(s->rows);
s->rowfree = NULL;
}
s->rows = NULL;
}
s->nrows = -1;
if (clrcols > 0) {
freep(&s->bindcols);
s->nbindcols = 0;
}
if (clrcols) {
freedyncols(s);
s->cols = NULL;
s->ncols = 0;
s->nowchar[1] = 0;
}
}
/**
* Reset bound columns to unbound state.
* @param s statement pointer
*/
static void
unbindcols(STMT *s)
{
int i;
s->bkmrkcol.type = -1;
s->bkmrkcol.max = 0;
s->bkmrkcol.lenp = NULL;
s->bkmrkcol.valp = NULL;
s->bkmrkcol.index = 0;
s->bkmrkcol.offs = 0;
for (i = 0; s->bindcols && i < s->nbindcols; i++) {
s->bindcols[i].type = -1;
s->bindcols[i].max = 0;
s->bindcols[i].lenp = NULL;
s->bindcols[i].valp = NULL;
s->bindcols[i].index = i;
s->bindcols[i].offs = 0;
}
}
/**
* Reallocate space for bound columns.
* @param s statement pointer
* @param ncols number of columns
* @result ODBC error code
*/
static SQLRETURN
mkbindcols(STMT *s, int ncols)
{
if (s->bindcols) {
if (s->nbindcols < ncols) {
int i;
BINDCOL *bindcols =
xrealloc(s->bindcols, ncols * sizeof (BINDCOL));
if (!bindcols) {
return nomem(s);
}
for (i = s->nbindcols; i < ncols; i++) {
bindcols[i].type = -1;
bindcols[i].max = 0;
bindcols[i].lenp = NULL;
bindcols[i].valp = NULL;
bindcols[i].index = i;
bindcols[i].offs = 0;
}
s->bindcols = bindcols;
s->nbindcols = ncols;
}
} else if (ncols > 0) {
s->bindcols = (BINDCOL *) xmalloc(ncols * sizeof (BINDCOL));
if (!s->bindcols) {
return nomem(s);
}
s->nbindcols = ncols;
unbindcols(s);
}
return SQL_SUCCESS;
}
/**
* Internal function to retrieve row data, used by SQLFetch() and
* friends and SQLGetData().
* @param s statement pointer
* @param col column number, 0 based
* @param otype output data type
* @param val output buffer
* @param len length of output buffer
* @param lenp output length
* @param partial flag for partial data retrieval
* @result ODBC error code
*/
static SQLRETURN
getrowdata(STMT *s, SQLUSMALLINT col, SQLSMALLINT otype,
SQLPOINTER val, SQLLEN len, SQLLEN *lenp, int partial)
{
char **data, valdummy[16];
SQLLEN dummy;
int valnull = 0;
int type = otype;
if (!lenp) {
lenp = &dummy;
}
if (!s->rows) {
*lenp = SQL_NULL_DATA;
return SQL_NO_DATA;
}
if (col >= s->ncols) {
setstat(s, -1, "invalid column", (*s->ov3) ? "07009" : "S1002");
return SQL_ERROR;
}
if (s->rowp < 0 || s->rowp >= s->nrows) {
*lenp = SQL_NULL_DATA;
return SQL_NO_DATA;
}
if (s->retr_data != SQL_RD_ON) {
return SQL_SUCCESS;
}
type = mapdeftype(type, s->cols[col].type, s->cols[col].nosign ? 1 : 0,
s->nowchar[0]);
#if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE)
/* MS Access hack part 3 (map SQL_C_DEFAULT to SQL_C_CHAR) */
if (type == SQL_C_WCHAR && otype == SQL_C_DEFAULT) {
type = SQL_C_CHAR;
}
#endif
#if HAVE_ENCDEC
if (type == SQL_C_CHAR) {
switch (s->cols[col].type) {
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
type = SQL_C_BINARY;
break;
}
#ifdef WINTERFACE
} else if (type == SQL_C_WCHAR) {
switch (s->cols[col].type) {
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
type = SQL_C_BINARY;
break;
}
#endif
}
#endif
data = s->rows + s->ncols + (s->rowp * s->ncols) + col;
if (!val) {
valnull = 1;
val = (SQLPOINTER) valdummy;
}
if (*data == NULL) {
*lenp = SQL_NULL_DATA;
switch (type) {
case SQL_C_UTINYINT:
case SQL_C_TINYINT:
case SQL_C_STINYINT:
#ifdef SQL_BIT
case SQL_C_BIT:
#endif
*((char *) val) = 0;
break;
case SQL_C_USHORT:
case SQL_C_SHORT:
case SQL_C_SSHORT:
*((short *) val) = 0;
break;
case SQL_C_ULONG:
case SQL_C_LONG:
case SQL_C_SLONG:
*((SQLINTEGER *) val) = 0;
break;
case SQL_C_FLOAT:
*((float *) val) = 0;
break;
case SQL_C_DOUBLE:
*((double *) val) = 0;
break;
case SQL_C_BINARY:
case SQL_C_CHAR:
#ifdef WINTERFACE
case SQL_C_WCHAR:
if (type == SQL_C_WCHAR) {
*((SQLWCHAR *) val) = '\0';
} else {
*((char *) val) = '\0';
}
#else
*((char *) val) = '\0';
#endif
break;
#ifdef SQL_C_TYPE_DATE
case SQL_C_TYPE_DATE:
#endif
case SQL_C_DATE:
memset((DATE_STRUCT *) val, 0, sizeof (DATE_STRUCT));
break;
#ifdef SQL_C_TYPE_TIME
case SQL_C_TYPE_TIME:
#endif
case SQL_C_TIME:
memset((TIME_STRUCT *) val, 0, sizeof (TIME_STRUCT));
break;
#ifdef SQL_C_TYPE_TIMESTAMP
case SQL_C_TYPE_TIMESTAMP:
#endif
case SQL_C_TIMESTAMP:
memset((TIMESTAMP_STRUCT *) val, 0, sizeof (TIMESTAMP_STRUCT));
break;
default:
return SQL_ERROR;
}
} else {
char *endp = NULL;
switch (type) {
case SQL_C_UTINYINT:
case SQL_C_TINYINT:
case SQL_C_STINYINT:
*((char *) val) = strtol(*data, &endp, 0);
if (endp && endp == *data) {
*lenp = SQL_NULL_DATA;
} else {
*lenp = sizeof (char);
}
break;
#ifdef SQL_BIT
case SQL_C_BIT:
*((char *) val) = getbool(*data);
*lenp = sizeof (char);
break;
#endif
case SQL_C_USHORT:
case SQL_C_SHORT:
case SQL_C_SSHORT:
*((short *) val) = strtol(*data, &endp, 0);
if (endp && endp == *data) {
*lenp = SQL_NULL_DATA;
} else {
*lenp = sizeof (short);
}
break;
case SQL_C_ULONG:
case SQL_C_LONG:
case SQL_C_SLONG:
*((SQLINTEGER *) val) = strtol(*data, &endp, 0);
if (endp && endp == *data) {
*lenp = SQL_NULL_DATA;
} else {
*lenp = sizeof (SQLINTEGER);
}
break;
case SQL_C_FLOAT:
*((float *) val) = ln_strtod(*data, &endp);
if (endp && endp == *data) {
*lenp = SQL_NULL_DATA;
} else {
*lenp = sizeof (float);
}
break;
case SQL_C_DOUBLE:
*((double *) val) = ln_strtod(*data, &endp);
if (endp && endp == *data) {
*lenp = SQL_NULL_DATA;
} else {
*lenp = sizeof (double);
}
break;
case SQL_C_BINARY:
#if HAVE_ENCDEC
{
int dlen, offs = 0;
char *bin;
if (*data == s->bincell && s->bincache) {
bin = s->bincache;
dlen = s->binlen;
} else {
freep(&s->bincache);
freep(&s->hexcache);
s->bincell = NULL;
dlen = strlen(*data);
bin = xmalloc(dlen + 1);
if (!bin) {
return nomem(s);
}
dlen = sqlite_decode_binary((unsigned char *) *data,
(unsigned char *) bin);
if (dlen < 0) {
freep(&bin);
setstat(s, -1, "error decoding binary data",
(*s->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
s->bincache = bin;
s->binlen = dlen;
s->bincell = *data;
}
if (partial && len && s->bindcols) {
if (s->bindcols[col].offs >= dlen) {
*lenp = 0;
if (!dlen && s->bindcols[col].offs == dlen) {
s->bindcols[col].offs = 1;
return SQL_SUCCESS;
}
s->bindcols[col].offs = 0;
return SQL_NO_DATA;
}
offs = s->bindcols[col].offs;
dlen -= offs;
}
if (val && !valnull && len) {
memcpy(val, bin + offs, min(len, dlen));
}
if (valnull || len < 1) {
*lenp = dlen;
} else {
*lenp = min(len, dlen);
if (*lenp == len && *lenp != dlen) {
*lenp = SQL_NO_TOTAL;
}
}
if (partial && len && s->bindcols) {
if (*lenp == SQL_NO_TOTAL) {
*lenp = dlen;
s->bindcols[col].offs += len;
setstat(s, -1, "data right truncated", "01004");
if (s->bindcols[col].lenp) {
*s->bindcols[col].lenp = dlen;
}
return SQL_SUCCESS_WITH_INFO;
}
s->bindcols[col].offs += *lenp;
}
if (*lenp == SQL_NO_TOTAL) {
*lenp = dlen;
setstat(s, -1, "data right truncated", "01004");
return SQL_SUCCESS_WITH_INFO;
}
break;
}
#endif
#ifdef WINTERFACE
case SQL_C_WCHAR:
#endif
case SQL_C_CHAR: {
int doz, zlen = len - 1;
int dlen = strlen(*data);
int offs = 0;
#ifdef WINTERFACE
SQLWCHAR *ucdata = NULL;
#endif
#if (defined(_WIN32) || defined(_WIN64)) && defined(WINTERFACE)
/* MS Access hack part 2 (reserved error -7748) */
if (!valnull &&
(s->cols == statSpec2P || s->cols == statSpec3P) &&
type == SQL_C_WCHAR) {
if (len > 0 && len <= sizeof (SQLWCHAR)) {
((char *) val)[0] = data[0][0];
memset((char *) val + 1, 0, len - 1);
*lenp = 1;
return SQL_SUCCESS;
}
}
#endif
#ifdef WINTERFACE
switch (type) {
case SQL_C_CHAR:
doz = 1;
break;
case SQL_C_WCHAR:
doz = sizeof (SQLWCHAR);
break;
default:
doz = 0;
break;
}
if (type == SQL_C_WCHAR) {
ucdata = uc_from_utf((SQLCHAR *) *data, dlen);
if (!ucdata) {
return nomem(s);
}
dlen = uc_strlen(ucdata) * sizeof (SQLWCHAR);
}
#else
doz = type == SQL_C_CHAR ? 1 : 0;
#endif
if (partial && len && s->bindcols) {
if (s->bindcols[col].offs >= dlen) {
#ifdef WINTERFACE
uc_free(ucdata);
#endif
*lenp = 0;
if (doz && val) {
#ifdef WINTERFACE
if (type == SQL_C_WCHAR) {
((SQLWCHAR *) val)[0] = 0;
} else {
((char *) val)[0] = '\0';
}
#else
((char *) val)[0] = '\0';
#endif
}
if (!dlen && s->bindcols[col].offs == dlen) {
s->bindcols[col].offs = 1;
return SQL_SUCCESS;
}
s->bindcols[col].offs = 0;
return SQL_NO_DATA;
}
offs = s->bindcols[col].offs;
dlen -= offs;
}
if (val && !valnull && len) {
#ifdef WINTERFACE
if (type == SQL_C_WCHAR) {
uc_strncpy(val, ucdata + offs / sizeof (SQLWCHAR),
(len - doz) / sizeof (SQLWCHAR));
} else {
strncpy(val, *data + offs, len - doz);
}
#else
strncpy(val, *data + offs, len - doz);
#endif
}
if (valnull || len < 1) {
*lenp = dlen;
} else {
*lenp = min(len - doz, dlen);
if (*lenp == len - doz && *lenp != dlen) {
*lenp = SQL_NO_TOTAL;
} else if (*lenp < zlen) {
zlen = *lenp;
}
}
if (len && !valnull && doz) {
#ifdef WINTERFACE
if (type == SQL_C_WCHAR) {
((SQLWCHAR *) val)[zlen / sizeof (SQLWCHAR)] = 0;
} else {
((char *) val)[zlen] = '\0';
}
#else
((char *) val)[zlen] = '\0';
#endif
}
#ifdef WINTERFACE
uc_free(ucdata);
#endif
if (partial && len && s->bindcols) {
if (*lenp == SQL_NO_TOTAL) {
*lenp = dlen;
s->bindcols[col].offs += len - doz;
setstat(s, -1, "data right truncated", "01004");
if (s->bindcols[col].lenp) {
*s->bindcols[col].lenp = dlen;
}
return SQL_SUCCESS_WITH_INFO;
}
s->bindcols[col].offs += *lenp;
}
if (*lenp == SQL_NO_TOTAL) {
*lenp = dlen;
setstat(s, -1, "data right truncated", "01004");
return SQL_SUCCESS_WITH_INFO;
}
break;
}
#ifdef SQL_C_TYPE_DATE
case SQL_C_TYPE_DATE:
#endif
case SQL_C_DATE:
if (str2date(*data, (DATE_STRUCT *) val) < 0) {
*lenp = SQL_NULL_DATA;
} else {
*lenp = sizeof (DATE_STRUCT);
}
break;
#ifdef SQL_C_TYPE_TIME
case SQL_C_TYPE_TIME:
#endif
case SQL_C_TIME:
if (str2time(*data, (TIME_STRUCT *) val) < 0) {
*lenp = SQL_NULL_DATA;
} else {
*lenp = sizeof (TIME_STRUCT);
}
break;
#ifdef SQL_C_TYPE_TIMESTAMP
case SQL_C_TYPE_TIMESTAMP:
#endif
case SQL_C_TIMESTAMP:
if (str2timestamp(*data, (TIMESTAMP_STRUCT *) val) < 0) {
*lenp = SQL_NULL_DATA;
} else {
*lenp = sizeof (TIMESTAMP_STRUCT);
}
break;
default:
return SQL_ERROR;
}
}
return SQL_SUCCESS;
}
/**
* Internal bind C variable to column of result set.
* @param stmt statement handle
* @param col column number, starting at 1
* @param type output type
* @param val output buffer
* @param max length of output buffer
* @param lenp output length pointer
* @result ODBC error code
*/
static SQLRETURN
drvbindcol(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type,
SQLPOINTER val, SQLLEN max, SQLLEN *lenp)
{
STMT *s;
int sz = 0;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (col < 1) {
if (col == 0 && s->bkmrk && type == SQL_C_BOOKMARK) {
s->bkmrkcol.type = type;
s->bkmrkcol.max = sizeof (SQLINTEGER);
s->bkmrkcol.lenp = lenp;
s->bkmrkcol.valp = val;
s->bkmrkcol.offs = 0;
if (lenp) {
*lenp = 0;
}
return SQL_SUCCESS;
}
setstat(s, -1, "invalid column", (*s->ov3) ? "07009" : "S1002");
return SQL_ERROR;
}
if (mkbindcols(s, col) != SQL_SUCCESS) {
return SQL_ERROR;
}
--col;
if (type == SQL_C_DEFAULT) {
type = mapdeftype(type, s->cols[col].type, 0,
s->nowchar[0] || s->nowchar[1]);
}
switch (type) {
case SQL_C_LONG:
case SQL_C_ULONG:
case SQL_C_SLONG:
sz = sizeof (SQLINTEGER);
break;
case SQL_C_TINYINT:
case SQL_C_UTINYINT:
case SQL_C_STINYINT:
sz = sizeof (SQLCHAR);
break;
case SQL_C_SHORT:
case SQL_C_USHORT:
case SQL_C_SSHORT:
sz = sizeof (short);
break;
case SQL_C_FLOAT:
sz = sizeof (SQLFLOAT);
break;
case SQL_C_DOUBLE:
sz = sizeof (SQLDOUBLE);
break;
case SQL_C_TIMESTAMP:
sz = sizeof (SQL_TIMESTAMP_STRUCT);
break;
case SQL_C_TIME:
sz = sizeof (SQL_TIME_STRUCT);
break;
case SQL_C_DATE:
sz = sizeof (SQL_DATE_STRUCT);
break;
case SQL_C_CHAR:
break;
#ifdef WINTERFACE
case SQL_C_WCHAR:
break;
#endif
#ifdef SQL_C_TYPE_DATE
case SQL_C_TYPE_DATE:
sz = sizeof (SQL_DATE_STRUCT);
break;
#endif
#ifdef SQL_C_TYPE_TIME
case SQL_C_TYPE_TIME:
sz = sizeof (SQL_TIME_STRUCT);
break;
#endif
#ifdef SQL_C_TYPE_TIMESTAMP
case SQL_C_TYPE_TIMESTAMP:
sz = sizeof (SQL_TIMESTAMP_STRUCT);
break;
#endif
#ifdef SQL_BIT
case SQL_C_BIT:
sz = sizeof (SQLCHAR);
break;
#endif
#if HAVE_ENCDEC
case SQL_C_BINARY:
break;
#endif
default:
if (val == NULL) {
/* fall through, unbinding column */
break;
}
setstat(s, -1, "invalid type %d", "HY003", type);
return SQL_ERROR;
}
if (val == NULL) {
/* unbind column */
s->bindcols[col].type = -1;
s->bindcols[col].max = 0;
s->bindcols[col].lenp = NULL;
s->bindcols[col].valp = NULL;
s->bindcols[col].offs = 0;
} else {
if (sz == 0 && max < 0) {
setstat(s, -1, "invalid length", "HY090");
return SQL_ERROR;
}
s->bindcols[col].type = type;
s->bindcols[col].max = (sz == 0) ? max : sz;
s->bindcols[col].lenp = lenp;
s->bindcols[col].valp = val;
s->bindcols[col].offs = 0;
if (lenp) {
*lenp = 0;
}
}
return SQL_SUCCESS;
}
/**
* Bind C variable to column of result set.
* @param stmt statement handle
* @param col column number, starting at 1
* @param type output type
* @param val output buffer
* @param max length of output buffer
* @param lenp output length pointer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLBindCol(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type,
SQLPOINTER val, SQLLEN max, SQLLEN *lenp)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvbindcol(stmt, col, type, val, max, lenp);
HSTMT_UNLOCK(stmt);
return ret;
}
/**
* Columns for result set of SQLTables().
*/
static COL tableSpec2[] = {
{ "SYSTEM", "COLUMN", "TABLE_QUALIFIER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "TABLE_OWNER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "TABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "COLUMN", "TABLE_TYPE", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "REMARKS", SCOL_VARCHAR, 50 }
};
static COL tableSpec3[] = {
{ "SYSTEM", "COLUMN", "TABLE_CAT", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "TABLE_SCHEM", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "TABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "COLUMN", "TABLE_TYPE", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "REMARKS", SCOL_VARCHAR, 50 }
};
/**
* Retrieve information on tables and/or views.
* @param stmt statement handle
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param type types of tables string or NULL
* @param typeLen length of types of tables string or SQL_NTS
* @result ODBC error code
*/
static SQLRETURN
drvtables(SQLHSTMT stmt,
SQLCHAR *cat, SQLSMALLINT catLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *table, SQLSMALLINT tableLen,
SQLCHAR *type, SQLSMALLINT typeLen)
{
SQLRETURN ret;
STMT *s;
DBC *d;
int ncols, asize, rc, size, npatt;
char *errp = NULL, tname[512];
char *where = "(type = 'table' or type = 'view')";
ret = mkresultset(stmt, tableSpec2, array_size(tableSpec2),
tableSpec3, array_size(tableSpec3), &asize);
if (ret != SQL_SUCCESS) {
return ret;
}
s = (STMT *) stmt;
d = (DBC *) s->dbc;
if (type && (typeLen > 0 || typeLen == SQL_NTS) && type[0] == '%') {
int size = 3 * asize;
s->rows = xmalloc(size * sizeof (char *));
if (!s->rows) {
s->nrows = 0;
return nomem(s);
}
memset(s->rows, 0, sizeof (char *) * size);
s->ncols = asize;
s->rows[s->ncols + 0] = "";
s->rows[s->ncols + 1] = "";
s->rows[s->ncols + 2] = "";
s->rows[s->ncols + 3] = "TABLE";
s->rows[s->ncols + 5] = "";
s->rows[s->ncols + 6] = "";
s->rows[s->ncols + 7] = "";
s->rows[s->ncols + 8] = "VIEW";
#ifdef MEMORY_DEBUG
s->rowfree = xfree__;
#else
s->rowfree = free;
#endif
s->nrows = 2;
s->rowp = -1;
return SQL_SUCCESS;
}
if (cat && (catLen > 0 || catLen == SQL_NTS) && cat[0] == '%') {
table = NULL;
goto doit;
}
if (schema && (schemaLen > 0 || schemaLen == SQL_NTS) &&
schema[0] == '%') {
if ((!cat || catLen == 0 || !cat[0]) &&
(!table || tableLen == 0 || !table[0])) {
table = NULL;
goto doit;
}
}
if (type && (typeLen > 0 || typeLen == SQL_NTS) && type[0] != '\0') {
char tmp[256], *t;
int with_view = 0, with_table = 0;
if (typeLen == SQL_NTS) {
strncpy(tmp, (char *) type, sizeof (tmp));
tmp[sizeof (tmp) - 1] = '\0';
} else {
int len = min(sizeof (tmp) - 1, typeLen);
strncpy(tmp, (char *) type, len);
tmp[len] = '\0';
}
t = tmp;
while (*t) {
*t = TOLOWER(*t);
t++;
}
t = tmp;
unescpat(t);
while (t) {
if (t[0] == '\'') {
++t;
}
if (strncmp(t, "table", 5) == 0) {
with_table++;
} else if (strncmp(t, "view", 4) == 0) {
with_view++;
}
t = strchr(t, ',');
if (t) {
++t;
}
}
if (with_view && with_table) {
/* where is already preset */
} else if (with_view && !with_table) {
where = "type = 'view'";
} else if (!with_view && with_table) {
where = "type = 'table'";
} else {
return SQL_SUCCESS;
}
}
doit:
if (!table) {
size = 1;
tname[0] = '%';
} else {
if (tableLen == SQL_NTS) {
size = sizeof (tname) - 1;
} else {
size = min(sizeof (tname) - 1, tableLen);
}
strncpy(tname, (char *) table, size);
}
tname[size] = '\0';
npatt = unescpat(tname);
ret = starttran(s);
if (ret != SQL_SUCCESS) {
return ret;
}
#if defined(_WIN32) || defined(_WIN64)
rc = sqlite_get_table_printf(d->sqlite,
"select %s as 'TABLE_QUALIFIER', "
"%s as 'TABLE_OWNER', "
"tbl_name as 'TABLE_NAME', "
"upper(type) as 'TABLE_TYPE', "
"NULL as 'REMARKS' "
"from sqlite_master where %s "
"and tbl_name %s '%q'",
&s->rows, &s->nrows, &ncols, &errp,
d->xcelqrx ? "''" : "NULL",
d->xcelqrx ? "'main'" : "NULL",
where, npatt ? "like" : "=", tname);
#else
rc = sqlite_get_table_printf(d->sqlite,
"select NULL as 'TABLE_QUALIFIER', "
"NULL as 'TABLE_OWNER', "
"tbl_name as 'TABLE_NAME', "
"upper(type) as 'TABLE_TYPE', "
"NULL as 'REMARKS' "
"from sqlite_master where %s "
"and tbl_name %s '%q'",
&s->rows, &s->nrows, &ncols, &errp,
where, npatt ? "like" : "=", tname);
#endif
if (rc == SQLITE_OK) {
if (ncols != s->ncols) {
freeresult(s, 0);
s->nrows = 0;
} else {
s->rowfree = sqlite_free_table;
}
} else {
s->nrows = 0;
s->rows = NULL;
s->rowfree = NULL;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
s->rowp = -1;
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Retrieve information on tables and/or views.
* @param stmt statement handle
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param type types of tables string or NULL
* @param typeLen length of types of tables string or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLTables(SQLHSTMT stmt,
SQLCHAR *cat, SQLSMALLINT catLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *table, SQLSMALLINT tableLen,
SQLCHAR *type, SQLSMALLINT typeLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvtables(stmt, cat, catLen, schema, schemaLen,
table, tableLen, type, typeLen);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Retrieve information on tables and/or views.
* @param stmt statement handle
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param type types of tables string or NULL
* @param typeLen length of types of tables string or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLTablesW(SQLHSTMT stmt,
SQLWCHAR *cat, SQLSMALLINT catLen,
SQLWCHAR *schema, SQLSMALLINT schemaLen,
SQLWCHAR *table, SQLSMALLINT tableLen,
SQLWCHAR *type, SQLSMALLINT typeLen)
{
char *c = NULL, *s = NULL, *t = NULL, *y = NULL;
SQLRETURN ret;
HSTMT_LOCK(stmt);
if (cat) {
c = uc_to_utf_c(cat, catLen);
if (!c) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (schema) {
s = uc_to_utf_c(schema, schemaLen);
if (!s) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (table) {
t = uc_to_utf_c(table, tableLen);
if (!t) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (type) {
y = uc_to_utf_c(type, typeLen);
if (!y) {
ret = nomem((STMT *) stmt);
goto done;
}
}
ret = drvtables(stmt, (SQLCHAR *) c, SQL_NTS, (SQLCHAR *) s, SQL_NTS,
(SQLCHAR *) t, SQL_NTS, (SQLCHAR *) y, SQL_NTS);
done:
HSTMT_UNLOCK(stmt);
uc_free(y);
uc_free(t);
uc_free(s);
uc_free(c);
return ret;
}
#endif
/**
* Columns for result set of SQLColumns().
*/
static COL colSpec2[] = {
{ "SYSTEM", "COLUMN", "TABLE_QUALIFIER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "TABLE_OWNER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "TABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "COLUMN", "COLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "COLUMN", "DATA_TYPE", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "TYPE_NAME", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "PRECISION", SQL_INTEGER, 50 },
{ "SYSTEM", "COLUMN", "LENGTH", SQL_INTEGER, 50 },
{ "SYSTEM", "COLUMN", "SCALE", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "RADIX", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "NULLABLE", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "REMARKS", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "COLUMN_DEF", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "SQL_DATA_TYPE", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "SQL_DATETIME_SUB", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "CHAR_OCTET_LENGTH", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "ORDINAL_POSITION", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "IS_NULLABLE", SCOL_VARCHAR, 50 }
};
static COL colSpec3[] = {
{ "SYSTEM", "COLUMN", "TABLE_CAT", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "TABLE_SCHEM", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "TABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "COLUMN", "COLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "COLUMN", "DATA_TYPE", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "TYPE_NAME", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "COLUMN_SIZE", SQL_INTEGER, 50 },
{ "SYSTEM", "COLUMN", "BUFFER_LENGTH", SQL_INTEGER, 50 },
{ "SYSTEM", "COLUMN", "DECIMAL_DIGITS", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "NUM_PREC_RADIX", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "NULLABLE", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "REMARKS", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "COLUMN_DEF", SCOL_VARCHAR, 50 },
{ "SYSTEM", "COLUMN", "SQL_DATA_TYPE", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "SQL_DATETIME_SUB", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "CHAR_OCTET_LENGTH", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "ORDINAL_POSITION", SQL_SMALLINT, 50 },
{ "SYSTEM", "COLUMN", "IS_NULLABLE", SCOL_VARCHAR, 50 }
};
/**
* Internal retrieve column information on table.
* @param stmt statement handle
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param col column name/pattern or NULL
* @param colLen length of column name/pattern or SQL_NTS
* @result ODBC error code
*/
static SQLRETURN
drvcolumns(SQLHSTMT stmt,
SQLCHAR *cat, SQLSMALLINT catLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *table, SQLSMALLINT tableLen,
SQLCHAR *col, SQLSMALLINT colLen)
{
SQLRETURN sret;
STMT *s;
DBC *d;
int ret, nrows, ncols, asize, i, k, roffs, namec;
int tnrows, tncols, npatt;
PTRDIFF_T size;
char *errp = NULL, tname[512], cname[512], **rowp, **trows;
sret = mkresultset(stmt, colSpec2, array_size(colSpec2),
colSpec3, array_size(colSpec3), &asize);
if (sret != SQL_SUCCESS) {
return sret;
}
s = (STMT *) stmt;
d = (DBC *) s->dbc;
if (!table) {
size = 1;
tname[0] = '%';
} else {
if (tableLen == SQL_NTS) {
size = sizeof (tname) - 1;
} else {
size = min(sizeof (tname) - 1, tableLen);
}
strncpy(tname, (char *) table, size);
}
tname[size] = '\0';
npatt = unescpat(tname);
size = 0;
if (col) {
if (colLen == SQL_NTS) {
size = sizeof (cname) - 1;
} else {
size = min(sizeof (cname) - 1, colLen);
}
strncpy(cname, (char *) col, size);
}
cname[size] = '\0';
if (!strcmp(cname, "%")) {
cname[0] = '\0';
}
sret = starttran(s);
if (sret != SQL_SUCCESS) {
return sret;
}
ret = sqlite_get_table_printf(d->sqlite,
"select tbl_name from sqlite_master where "
"(type = 'table' or type = 'view') "
"and tbl_name %s '%q'",
&trows, &tnrows, &tncols, &errp,
npatt ? "like" : "=", tname);
if (ret != SQLITE_OK) {
setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", ret);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_ERROR;
}
/* pass 1; compute number of rows of result set */
if (tncols * tnrows <= 0) {
sqlite_free_table(rowp);
return SQL_SUCCESS;
}
size = 0;
for (i = 1; i <= tnrows; i++) {
ret = sqlite_get_table_printf(d->sqlite, "PRAGMA table_info('%q')",
&rowp, &nrows, &ncols, &errp, trows[i]);
if (ret != SQLITE_OK) {
setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", ret);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_ERROR;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
if (ncols * nrows > 0) {
namec = -1;
for (k = 0; k < ncols; k++) {
if (strcmp(rowp[k], "name") == 0) {
namec = k;
break;
}
}
if (cname[0]) {
for (k = 1; k <= nrows; k++) {
if (namematch(rowp[k * ncols + namec], cname, 1)) {
size++;
}
}
} else {
size += nrows;
}
}
sqlite_free_table(rowp);
}
/* pass 2: fill result set */
if (size <= 0) {
sqlite_free_table(trows);
return SQL_SUCCESS;
}
s->nrows = size;
size = (size + 1) * asize;
s->rows = xmalloc((size + 1) * sizeof (char *));
if (!s->rows) {
s->nrows = 0;
sqlite_free_table(trows);
return nomem(s);
}
s->rows[0] = (char *) size;
s->rows += 1;
memset(s->rows, 0, sizeof (char *) * size);
s->rowfree = freerows;
roffs = 1;
for (i = 1; i <= tnrows; i++) {
ret = sqlite_get_table_printf(d->sqlite, "PRAGMA table_info('%q')",
&rowp, &nrows, &ncols, &errp, trows[i]);
if (ret != SQLITE_OK) {
setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", ret);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
sqlite_free_table(trows);
return SQL_ERROR;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
if (ncols * nrows > 0) {
int m, mr, nr = nrows;
namec = -1;
for (k = 0; k < ncols; k++) {
if (strcmp(rowp[k], "name") == 0) {
namec = k;
break;
}
}
if (cname[0]) {
nr = 0;
for (k = 1; k <= nrows; k++) {
if (namematch(rowp[k * ncols + namec], cname, 1)) {
nr++;
}
}
}
for (k = 0; k < nr; k++) {
m = asize * (roffs + k);
s->rows[m + 0] = xstrdup("");
#if defined(_WIN32) || defined(_WIN64)
s->rows[m + 1] = xstrdup(d->xcelqrx ? "main" : "");
#else
s->rows[m + 1] = xstrdup("");
#endif
s->rows[m + 2] = xstrdup(trows[i]);
s->rows[m + 8] = xstrdup("10");
s->rows[m + 9] = xstrdup("0");
s->rows[m + 15] = xstrdup("16384");
}
for (k = 0; nr && k < ncols; k++) {
if (strcmp(rowp[k], "cid") == 0) {
for (mr = 0, m = 1; m <= nrows; m++) {
char buf[256];
int ir, coln = i;
if (cname[0] &&
!namematch(rowp[m * ncols + namec], cname, 1)) {
continue;
}
ir = asize * (roffs + mr);
sscanf(rowp[m * ncols + k], "%d", &coln);
sprintf(buf, "%d", coln + 1);
s->rows[ir + 16] = xstrdup(buf);
++mr;
}
} else if (k == namec) {
for (mr = 0, m = 1; m <= nrows; m++) {
int ir;
if (cname[0] &&
!namematch(rowp[m * ncols + namec], cname, 1)) {
continue;
}
ir = asize * (roffs + mr);
s->rows[ir + 3] = xstrdup(rowp[m * ncols + k]);
++mr;
}
} else if (strcmp(rowp[k], "notnull") == 0) {
for (mr = 0, m = 1; m <= nrows; m++) {
int ir;
if (cname[0] &&
!namematch(rowp[m * ncols + namec], cname, 1)) {
continue;
}
ir = asize * (roffs + mr);
if (*rowp[m * ncols + k] != '0') {
s->rows[ir + 10] = xstrdup(stringify(SQL_FALSE));
} else {
s->rows[ir + 10] = xstrdup(stringify(SQL_TRUE));
}
s->rows[ir + 17] =
xstrdup((*rowp[m * ncols + k] != '0') ?
"NO" : "YES");
++mr;
}
} else if (strcmp(rowp[k], "dflt_value") == 0) {
for (mr = 0, m = 1; m <= nrows; m++) {
char *dflt = rowp[m * ncols + k];
int ir;
if (cname[0] &&
!namematch(rowp[m * ncols + namec], cname, 1)) {
continue;
}
ir = asize * (roffs + mr);
s->rows[ir + 12] = xstrdup(dflt ? dflt : "NULL");
++mr;
}
} else if (strcmp(rowp[k], "type") == 0) {
for (mr = 0, m = 1; m <= nrows; m++) {
char *typename = rowp[m * ncols + k];
int sqltype, mm, dd, ir;
char buf[256];
if (cname[0] &&
!namematch(rowp[m * ncols + namec], cname, 1)) {
continue;
}
ir = asize * (roffs + mr);
s->rows[ir + 5] = xstrdup(typename);
sqltype = mapsqltype(typename, NULL, *s->ov3,
s->nowchar[0]);
getmd(typename, sqltype, &mm, &dd);
#ifdef SQL_LONGVARCHAR
if (sqltype == SQL_VARCHAR && mm > 255) {
sqltype = SQL_LONGVARCHAR;
}
#endif
#ifdef WINTERFACE
#ifdef SQL_WLONGVARCHAR
if (sqltype == SQL_WVARCHAR && mm > 255) {
sqltype = SQL_WLONGVARCHAR;
}
#endif
#endif
#if HAVE_ENCDEC
if (sqltype == SQL_VARBINARY && mm > 255) {
sqltype = SQL_LONGVARBINARY;
}
#endif
sprintf(buf, "%d", sqltype);
s->rows[ir + 4] = xstrdup(buf);
s->rows[ir + 13] = xstrdup(buf);
sprintf(buf, "%d", mm);
s->rows[ir + 7] = xstrdup(buf);
sprintf(buf, "%d", dd);
s->rows[ir + 6] = xstrdup(buf);
++mr;
}
}
}
roffs += nr;
}
sqlite_free_table(rowp);
}
sqlite_free_table(trows);
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Retrieve column information on table.
* @param stmt statement handle
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param col column name/pattern or NULL
* @param colLen length of column name/pattern or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLColumns(SQLHSTMT stmt,
SQLCHAR *cat, SQLSMALLINT catLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *table, SQLSMALLINT tableLen,
SQLCHAR *col, SQLSMALLINT colLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvcolumns(stmt, cat, catLen, schema, schemaLen,
table, tableLen, col, colLen);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Retrieve column information on table (UNICODE version).
* @param stmt statement handle
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param col column name/pattern or NULL
* @param colLen length of column name/pattern or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLColumnsW(SQLHSTMT stmt,
SQLWCHAR *cat, SQLSMALLINT catLen,
SQLWCHAR *schema, SQLSMALLINT schemaLen,
SQLWCHAR *table, SQLSMALLINT tableLen,
SQLWCHAR *col, SQLSMALLINT colLen)
{
char *c = NULL, *s = NULL, *t = NULL, *k = NULL;
SQLRETURN ret;
HSTMT_LOCK(stmt);
if (cat) {
c = uc_to_utf_c(cat, catLen);
if (!c) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (schema) {
s = uc_to_utf_c(schema, schemaLen);
if (!s) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (table) {
t = uc_to_utf_c(table, tableLen);
if (!t) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (col) {
k = uc_to_utf_c(col, colLen);
if (!k) {
ret = nomem((STMT *) stmt);
goto done;
}
}
ret = drvcolumns(stmt, (SQLCHAR *) c, SQL_NTS, (SQLCHAR *) s, SQL_NTS,
(SQLCHAR *) t, SQL_NTS, (SQLCHAR *) k, SQL_NTS);
done:
HSTMT_UNLOCK(stmt);
uc_free(k);
uc_free(t);
uc_free(s);
uc_free(c);
return ret;
}
#endif
/**
* Columns for result set of SQLGetTypeInfo().
*/
static COL typeSpec2[] = {
{ "SYSTEM", "TYPE", "TYPE_NAME", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TYPE", "DATA_TYPE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "PRECISION", SQL_INTEGER, 9 },
{ "SYSTEM", "TYPE", "LITERAL_PREFIX", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TYPE", "LITERAL_SUFFIX", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TYPE", "CREATE_PARAMS", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TYPE", "NULLABLE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "CASE_SENSITIVE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "SEARCHABLE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "UNSIGNED_ATTRIBUTE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "MONEY", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "AUTO_INCREMENT", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "LOCAL_TYPE_NAME", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TYPE", "MINIMUM_SCALE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "MAXIMUM_SCALE", SQL_SMALLINT, 2 }
};
static COL typeSpec3[] = {
{ "SYSTEM", "TYPE", "TYPE_NAME", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TYPE", "DATA_TYPE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "COLUMN_SIZE", SQL_INTEGER, 9 },
{ "SYSTEM", "TYPE", "LITERAL_PREFIX", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TYPE", "LITERAL_SUFFIX", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TYPE", "CREATE_PARAMS", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TYPE", "NULLABLE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "CASE_SENSITIVE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "SEARCHABLE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "UNSIGNED_ATTRIBUTE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "FIXED_PREC_SCALE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "AUTO_UNIQUE_VALUE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "LOCAL_TYPE_NAME", SCOL_VARCHAR, 50 },
{ "SYSTEM", "TYPE", "MINIMUM_SCALE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "MAXIMUM_SCALE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "SQL_DATA_TYPE", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "SQL_DATETIME_SUB", SQL_SMALLINT, 2 },
{ "SYSTEM", "TYPE", "NUM_PREC_RADIX", SQL_INTEGER, 4 },
{ "SYSTEM", "TYPE", "INTERVAL_PRECISION", SQL_SMALLINT, 2 }
};
/**
* Internal function to build up data type information as row in result set.
* @param s statement pointer
* @param row row number
* @param asize number of items in a row
* @param typename name of type
* @param type integer SQL type
* @param tind type index
*/
static void
mktypeinfo(STMT *s, int row, int asize, char *typename, int type, int tind)
{
int offs = row * asize;
char *tcode, *crpar = NULL, *quote = NULL, *sign = stringify(SQL_FALSE);
static char tcodes[32 * 32];
if (tind <= 0) {
tind = row;
}
tcode = tcodes + tind * 32;
sprintf(tcode, "%d", type);
s->rows[offs + 0] = typename;
s->rows[offs + 1] = tcode;
if (asize >= 17) {
s->rows[offs + 15] = tcode;
s->rows[offs + 16] = "0";
}
switch (type) {
default:
#ifdef SQL_LONGVARCHAR
case SQL_LONGVARCHAR:
#ifdef WINTERFACE
case SQL_WLONGVARCHAR:
#endif
crpar = "length";
quote = "'";
sign = NULL;
s->rows[offs + 2] = "65536";
break;
#endif
case SQL_CHAR:
case SQL_VARCHAR:
#ifdef WINTERFACE
case SQL_WCHAR:
case SQL_WVARCHAR:
#endif
s->rows[offs + 2] = "255";
crpar = "length";
quote = "'";
sign = NULL;
break;
case SQL_TINYINT:
s->rows[offs + 2] = "3";
break;
case SQL_SMALLINT:
s->rows[offs + 2] = "5";
break;
case SQL_INTEGER:
s->rows[offs + 2] = "9";
break;
case SQL_FLOAT:
s->rows[offs + 2] = "7";
break;
case SQL_DOUBLE:
s->rows[offs + 2] = "15";
break;
#ifdef SQL_TYPE_DATE
case SQL_TYPE_DATE:
#endif
case SQL_DATE:
s->rows[offs + 2] = "10";
quote = "'";
sign = NULL;
break;
#ifdef SQL_TYPE_TIME
case SQL_TYPE_TIME:
#endif
case SQL_TIME:
s->rows[offs + 2] = "8";
quote = "'";
sign = NULL;
break;
#ifdef SQL_TYPE_TIMESTAMP
case SQL_TYPE_TIMESTAMP:
#endif
case SQL_TIMESTAMP:
s->rows[offs + 2] = "32";
quote = "'";
sign = NULL;
break;
#if HAVE_ENCDEC
case SQL_VARBINARY:
sign = NULL;
s->rows[offs + 2] = "255";
break;
case SQL_LONGVARBINARY:
sign = NULL;
s->rows[offs + 2] = "65536";
break;
#endif
#ifdef SQL_BIT
case SQL_BIT:
sign = NULL;
s->rows[offs + 2] = "1";
break;
#endif
}
s->rows[offs + 3] = s->rows[offs + 4] = quote;
s->rows[offs + 5] = crpar;
s->rows[offs + 6] = stringify(SQL_NULLABLE);
s->rows[offs + 7] = stringify(SQL_FALSE);
s->rows[offs + 8] = stringify(SQL_SEARCHABLE);
s->rows[offs + 9] = sign;
s->rows[offs + 10] = stringify(SQL_FALSE);
s->rows[offs + 11] = stringify(SQL_FALSE);
s->rows[offs + 12] = typename;
switch (type) {
case SQL_DATE:
case SQL_TIME:
s->rows[offs + 13] = "0";
s->rows[offs + 14] = "0";
break;
#ifdef SQL_TYPE_TIMESTAMP
case SQL_TYPE_TIMESTAMP:
#endif
case SQL_TIMESTAMP:
s->rows[offs + 13] = "0";
s->rows[offs + 14] = "3";
break;
default:
s->rows[offs + 13] = NULL;
s->rows[offs + 14] = NULL;
break;
}
}
/**
* Helper function to sort type information.
* Callback for qsort().
* @param a first item to compare
* @param b second item to compare
* @result ==0, <0, >0 according to data type number
*/
static int
typeinfosort(const void *a, const void *b)
{
char **pa = (char **) a;
char **pb = (char **) b;
int na, nb;
na = strtol(pa[1], NULL, 0);
nb = strtol(pb[1], NULL, 0);
return na - nb;
}
/**
* Internal return data type information.
* @param stmt statement handle
* @param sqltype which type to retrieve
* @result ODBC error code
*/
static SQLRETURN
drvgettypeinfo(SQLHSTMT stmt, SQLSMALLINT sqltype)
{
SQLRETURN ret;
STMT *s;
DBC *d;
int asize;
ret = mkresultset(stmt, typeSpec2, array_size(typeSpec2),
typeSpec3, array_size(typeSpec3), &asize);
if (ret != SQL_SUCCESS) {
return ret;
}
s = (STMT *) stmt;
d = (DBC *) s->dbc;
#ifdef WINTERFACE
#ifdef SQL_LONGVARCHAR
#ifdef SQL_WLONGVARCHAR
if (s->nowchar[0]) {
s->nrows = (sqltype == SQL_ALL_TYPES) ? 13 : 1;
} else {
s->nrows = (sqltype == SQL_ALL_TYPES) ? 17 : 1;
}
#else
if (s->nowchar[0]) {
s->nrows = (sqltype == SQL_ALL_TYPES) ? 12 : 1;
} else {
s->nrows = (sqltype == SQL_ALL_TYPES) ? 16 : 1;
}
#endif
#else
s->nrows = (sqltype == SQL_ALL_TYPES) ? 15 : 1;
#endif
#else
#ifdef SQL_LONGVARCHAR
s->nrows = (sqltype == SQL_ALL_TYPES) ? 13 : 1;
#else
s->nrows = (sqltype == SQL_ALL_TYPES) ? 12 : 1;
#endif
#endif
#if HAVE_ENCDEC
if (sqltype == SQL_ALL_TYPES) {
s->nrows += 2;
}
#endif
#ifdef SQL_BIT
if (sqltype == SQL_ALL_TYPES) {
s->nrows += 1;
}
#endif
s->rows = (char **) xmalloc(sizeof (char *) * (s->nrows + 1) * asize);
if (!s->rows) {
s->nrows = 0;
return nomem(s);
}
#ifdef MEMORY_DEBUG
s->rowfree = xfree__;
#else
s->rowfree = free;
#endif
memset(s->rows, 0, sizeof (char *) * (s->nrows + 1) * asize);
if (sqltype == SQL_ALL_TYPES) {
int cc = 1;
mktypeinfo(s, cc++, asize, "varchar", SQL_VARCHAR, 0);
mktypeinfo(s, cc++, asize, "tinyint", SQL_TINYINT, 0);
mktypeinfo(s, cc++, asize, "smallint", SQL_SMALLINT, 0);
mktypeinfo(s, cc++, asize, "integer", SQL_INTEGER, 0);
mktypeinfo(s, cc++, asize, "float", SQL_FLOAT, 0);
mktypeinfo(s, cc++, asize, "double", SQL_DOUBLE, 0);
#ifdef SQL_TYPE_DATE
mktypeinfo(s, cc++, asize, "date",
(*s->ov3) ? SQL_TYPE_DATE : SQL_DATE, 0);
#else
mktypeinfo(s, cc++, asize, "date", SQL_DATE, 0);
#endif
#ifdef SQL_TYPE_TIME
mktypeinfo(s, cc++, asize, "time",
(*s->ov3) ? SQL_TYPE_TIME : SQL_TIME, 0);
#else
mktypeinfo(s, cc++, asize, "time", SQL_TIME, 0);
#endif
#ifdef SQL_TYPE_TIMESTAMP
mktypeinfo(s, cc++, asize, "timestamp",
(*s->ov3) ? SQL_TYPE_TIMESTAMP : SQL_TIMESTAMP, 0);
#else
mktypeinfo(s, cc++, asize, "timestamp", SQL_TIMESTAMP, 0);
#endif
mktypeinfo(s, cc++, asize, "char", SQL_CHAR, 0);
mktypeinfo(s, cc++, asize, "numeric", SQL_DOUBLE, 0);
#ifdef SQL_LONGVARCHAR
mktypeinfo(s, cc++, asize, "text", SQL_LONGVARCHAR, 0);
mktypeinfo(s, cc++, asize, "longvarchar", SQL_LONGVARCHAR, 0);
#else
mktypeinfo(s, cc++, asize, "text", SQL_VARCHAR, 0);
#endif
#ifdef WINTERFACE
if (!s->nowchar[0]) {
mktypeinfo(s, cc++, asize, "wvarchar", SQL_WVARCHAR, 0);
#ifdef SQL_LONGVARCHAR
mktypeinfo(s, cc++, asize, "wchar", SQL_WCHAR, 0);
mktypeinfo(s, cc++, asize, "wtext", SQL_WLONGVARCHAR, 0);
#ifdef SQL_WLONGVARCHAR
mktypeinfo(s, cc++, asize, "longwvarchar", SQL_WLONGVARCHAR, 0);
#endif
#else
mktypeinfo(s, cc++, asize, "wvarchar", SQL_WVARCHAR, 0);
mktypeinfo(s, cc++, asize, "wchar", SQL_WCHAR, 0);
mktypeinfo(s, cc++, asize, "wtext", SQL_WVARCHAR, 0);
#endif
}
#endif
#if HAVE_ENCDEC
mktypeinfo(s, cc++, asize, "varbinary", SQL_VARBINARY, 0);
mktypeinfo(s, cc++, asize, "longvarbinary", SQL_LONGVARBINARY, 0);
#endif
#ifdef SQL_BIT
mktypeinfo(s, cc++, asize, "bit", SQL_BIT, 0);
#endif
qsort(s->rows + asize, s->nrows, sizeof (char *) * asize,
typeinfosort);
} else {
switch (sqltype) {
case SQL_CHAR:
mktypeinfo(s, 1, asize, "char", SQL_CHAR, 10);
break;
case SQL_VARCHAR:
mktypeinfo(s, 1, asize, "varchar", SQL_VARCHAR, 1);
break;
case SQL_TINYINT:
mktypeinfo(s, 1, asize, "tinyint", SQL_TINYINT, 2);
break;
case SQL_SMALLINT:
mktypeinfo(s, 1, asize, "smallint", SQL_SMALLINT, 3);
break;
case SQL_INTEGER:
mktypeinfo(s, 1, asize, "integer", SQL_INTEGER, 4);
break;
case SQL_FLOAT:
mktypeinfo(s, 1, asize, "float", SQL_FLOAT, 5);
break;
case SQL_DOUBLE:
mktypeinfo(s, 1, asize, "double", SQL_DOUBLE, 6);
break;
#ifdef SQL_TYPE_DATE
case SQL_TYPE_DATE:
mktypeinfo(s, 1, asize, "date", SQL_TYPE_DATE, 25);
break;
#endif
case SQL_DATE:
mktypeinfo(s, 1, asize, "date", SQL_DATE, 7);
break;
#ifdef SQL_TYPE_TIME
case SQL_TYPE_TIME:
mktypeinfo(s, 1, asize, "time", SQL_TYPE_TIME, 26);
break;
#endif
case SQL_TIME:
mktypeinfo(s, 1, asize, "time", SQL_TIME, 8);
break;
#ifdef SQL_TYPE_TIMESTAMP
case SQL_TYPE_TIMESTAMP:
mktypeinfo(s, 1, asize, "timestamp", SQL_TYPE_TIMESTAMP, 27);
break;
#endif
case SQL_TIMESTAMP:
mktypeinfo(s, 1, asize, "timestamp", SQL_TIMESTAMP, 9);
break;
#ifdef SQL_LONGVARCHAR
case SQL_LONGVARCHAR:
mktypeinfo(s, 1, asize, "longvarchar", SQL_LONGVARCHAR, 12);
break;
#endif
#ifdef WINTERFACE
#ifdef SQL_WCHAR
case SQL_WCHAR:
mktypeinfo(s, 1, asize, "wchar", SQL_WCHAR, 18);
break;
#endif
#ifdef SQL_WVARCHAR
case SQL_WVARCHAR:
mktypeinfo(s, 1, asize, "wvarchar", SQL_WVARCHAR, 19);
break;
#endif
#ifdef SQL_WLONGVARCHAR
case SQL_WLONGVARCHAR:
mktypeinfo(s, 1, asize, "longwvarchar", SQL_WLONGVARCHAR, 20);
break;
#endif
#endif
#if HAVE_ENCDEC
case SQL_VARBINARY:
mktypeinfo(s, 1, asize, "varbinary", SQL_VARBINARY, 30);
break;
case SQL_LONGVARBINARY:
mktypeinfo(s, 1, asize, "longvarbinary", SQL_LONGVARBINARY, 31);
break;
#endif
#ifdef SQL_BIT
case SQL_BIT:
mktypeinfo(s, 1, asize, "bit", SQL_BIT, 29);
break;
#endif
default:
s->nrows = 0;
}
}
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Return data type information.
* @param stmt statement handle
* @param sqltype which type to retrieve
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetTypeInfo(SQLHSTMT stmt, SQLSMALLINT sqltype)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvgettypeinfo(stmt, sqltype);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Return data type information (UNICODE version).
* @param stmt statement handle
* @param sqltype which type to retrieve
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetTypeInfoW(SQLHSTMT stmt, SQLSMALLINT sqltype)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvgettypeinfo(stmt, sqltype);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
/**
* Columns for result set of SQLStatistics().
*/
static COL statSpec2[] = {
{ "SYSTEM", "STATISTICS", "TABLE_QUALIFIER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "STATISTICS", "TABLE_OWNER", SCOL_VARCHAR, 50 },
{ "SYSTEM", "STATISTICS", "TABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "STATISTICS", "NON_UNIQUE", SQL_SMALLINT, 50 },
{ "SYSTEM", "STATISTICS", "INDEX_QUALIFIER", SCOL_VARCHAR, 255 },
{ "SYSTEM", "STATISTICS", "INDEX_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "STATISTICS", "TYPE", SQL_SMALLINT, 50 },
{ "SYSTEM", "STATISTICS", "SEQ_IN_INDEX", SQL_SMALLINT, 50 },
{ "SYSTEM", "STATISTICS", "COLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "STATISTICS", "COLLATION", SCOL_CHAR, 1 },
{ "SYSTEM", "STATISTICS", "CARDINALITY", SQL_INTEGER, 50 },
{ "SYSTEM", "STATISTICS", "PAGES", SQL_INTEGER, 50 },
{ "SYSTEM", "STATISTICS", "FILTER_CONDITION", SCOL_VARCHAR, 255 }
};
static COL statSpec3[] = {
{ "SYSTEM", "STATISTICS", "TABLE_CAT", SCOL_VARCHAR, 50 },
{ "SYSTEM", "STATISTICS", "TABLE_SCHEM", SCOL_VARCHAR, 50 },
{ "SYSTEM", "STATISTICS", "TABLE_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "STATISTICS", "NON_UNIQUE", SQL_SMALLINT, 50 },
{ "SYSTEM", "STATISTICS", "INDEX_QUALIFIER", SCOL_VARCHAR, 255 },
{ "SYSTEM", "STATISTICS", "INDEX_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "STATISTICS", "TYPE", SQL_SMALLINT, 50 },
{ "SYSTEM", "STATISTICS", "ORDINAL_POSITION", SQL_SMALLINT, 50 },
{ "SYSTEM", "STATISTICS", "COLUMN_NAME", SCOL_VARCHAR, 255 },
{ "SYSTEM", "STATISTICS", "ASC_OR_DESC", SCOL_CHAR, 1 },
{ "SYSTEM", "STATISTICS", "CARDINALITY", SQL_INTEGER, 50 },
{ "SYSTEM", "STATISTICS", "PAGES", SQL_INTEGER, 50 },
{ "SYSTEM", "STATISTICS", "FILTER_CONDITION", SCOL_VARCHAR, 255 }
};
/**
* Internal return statistic information on table indices.
* @param stmt statement handle
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param itype type of index information
* @param resv reserved
* @result ODBC error code
*/
static SQLRETURN
drvstatistics(SQLHSTMT stmt, SQLCHAR *cat, SQLSMALLINT catLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *table, SQLSMALLINT tableLen,
SQLUSMALLINT itype, SQLUSMALLINT resv)
{
SQLRETURN sret;
STMT *s;
DBC *d;
int i, asize, ret, nrows, ncols, offs, namec, uniquec, addipk = 0;
PTRDIFF_T size;
char **rowp, *errp = NULL, tname[512];
sret = mkresultset(stmt, statSpec2, array_size(statSpec2),
statSpec3, array_size(statSpec3), &asize);
if (sret != SQL_SUCCESS) {
return sret;
}
s = (STMT *) stmt;
d = (DBC *) s->dbc;
if (!table || table[0] == '\0' || table[0] == '%') {
setstat(s, -1, "need table name", (*s->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
if (tableLen == SQL_NTS) {
size = sizeof (tname) - 1;
} else {
size = min(sizeof (tname) - 1, tableLen);
}
strncpy(tname, (char *) table, size);
tname[size] = '\0';
unescpat(tname);
sret = starttran(s);
if (sret != SQL_SUCCESS) {
return sret;
}
/*
* Try integer primary key (autoincrement) first
*/
if (itype == SQL_INDEX_UNIQUE || itype == SQL_INDEX_ALL) {
int colid, typec, npk = 0;
rowp = 0;
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA table_info('%q')", &rowp,
&nrows, &ncols, NULL, tname);
if (ret != SQLITE_OK) {
goto noipk;
}
namec = findcol(rowp, ncols, "name");
uniquec = findcol(rowp, ncols, "pk");
typec = findcol(rowp, ncols, "type");
colid = findcol(rowp, ncols, "cid");
if (namec < 0 || uniquec < 0 || typec < 0 || colid < 0) {
goto noipk;
}
for (i = 1; i <= nrows; i++) {
if (*rowp[i * ncols + uniquec] != '0' &&
strlen(rowp[i * ncols + typec]) == 7 &&
strncasecmp(rowp[i * ncols + typec], "integer", 7)
== 0) {
npk++;
}
}
if (npk == 1) {
addipk = 1;
}
noipk:
sqlite_free_table(rowp);
}
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA index_list('%q')", &rowp,
&nrows, &ncols, &errp, tname);
if (ret != SQLITE_OK) {
setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", ret);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_ERROR;
}
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
size = 0;
namec = findcol(rowp, ncols, "name");
uniquec = findcol(rowp, ncols, "unique");
if (namec < 0 || uniquec < 0) {
goto nodata;
}
for (i = 1; i <= nrows; i++) {
int nnrows, nncols;
char **rowpp;
int isuniq;
isuniq = *rowp[i * ncols + uniquec] != '0';
if (isuniq || itype == SQL_INDEX_ALL) {
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA index_info('%q')", &rowpp,
&nnrows, &nncols, NULL,
rowp[i * ncols + namec]);
if (ret == SQLITE_OK) {
size += nnrows;
sqlite_free_table(rowpp);
}
}
}
nodata:
if (addipk) {
size++;
}
if (size == 0) {
sqlite_free_table(rowp);
return SQL_SUCCESS;
}
s->nrows = size;
size = (size + 1) * asize;
s->rows = xmalloc((size + 1) * sizeof (char *));
if (!s->rows) {
s->nrows = 0;
return nomem(s);
}
s->rows[0] = (char *) size;
s->rows += 1;
memset(s->rows, 0, sizeof (char *) * size);
s->rowfree = freerows;
offs = 0;
if (addipk) {
char **rowpp;
int ncols2, nrows2;
rowpp = 0;
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA table_info('%q')", &rowpp,
&nrows2, &ncols2, NULL, tname);
if (ret == SQLITE_OK) {
int namecc, uniquecc, colid, typec, roffs;
namecc = findcol(rowpp, ncols2, "name");
uniquecc = findcol(rowpp, ncols2, "pk");
typec = findcol(rowpp, ncols2, "type");
colid = findcol(rowpp, ncols2, "cid");
if (namecc < 0 || uniquecc < 0 || typec < 0 || colid < 0) {
addipk = 0;
s->nrows--;
goto nodata2;
}
for (i = 1; i <= nrows2; i++) {
if (*rowpp[i * ncols2 + uniquecc] != '0' &&
strlen(rowpp[i * ncols2 + typec]) == 7 &&
strncasecmp(rowpp[i * ncols2 + typec], "integer", 7)
== 0) {
break;
}
}
if (i > nrows) {
addipk = 0;
s->nrows--;
goto nodata2;
}
roffs = s->ncols;
s->rows[roffs + 0] = xstrdup("");
#if defined(_WIN32) || defined(_WIN64)
s->rows[roffs + 1] = xstrdup(d->xcelqrx ? "main" : "");
#else
s->rows[roffs + 1] = xstrdup("");
#endif
s->rows[roffs + 2] = xstrdup(tname);
s->rows[roffs + 3] = xstrdup(stringify(SQL_FALSE));
s->rows[roffs + 5] = xstrdup("(autoindex 0)");
s->rows[roffs + 6] = xstrdup(stringify(SQL_INDEX_OTHER));
s->rows[roffs + 7] = xstrdup("1");
s->rows[roffs + 8] = xstrdup(rowpp[i * ncols2 + namecc]);
s->rows[roffs + 9] = xstrdup("A");
}
nodata2:
sqlite_free_table(rowpp);
}
for (i = 1; i <= nrows; i++) {
int nnrows, nncols;
char **rowpp;
if (*rowp[i * ncols + uniquec] != '0' || itype == SQL_INDEX_ALL) {
int k;
ret = sqlite_get_table_printf(d->sqlite,
"PRAGMA index_info('%q')", &rowpp,
&nnrows, &nncols, NULL,
rowp[i * ncols + namec]);
if (ret != SQLITE_OK) {
continue;
}
for (k = 0; nnrows && k < nncols; k++) {
if (strcmp(rowpp[k], "name") == 0) {
int m;
for (m = 1; m <= nnrows; m++) {
int roffs = (offs + addipk + m) * s->ncols;
int isuniq;
isuniq = *rowp[i * ncols + uniquec] != '0';
s->rows[roffs + 0] = xstrdup("");
s->rows[roffs + 1] = xstrdup("");
s->rows[roffs + 2] = xstrdup(tname);
if (isuniq) {
s->rows[roffs + 3] = xstrdup(stringify(SQL_FALSE));
} else {
s->rows[roffs + 3] = xstrdup(stringify(SQL_TRUE));
}
s->rows[roffs + 5] = xstrdup(rowp[i * ncols + namec]);
s->rows[roffs + 6] =
xstrdup(stringify(SQL_INDEX_OTHER));
s->rows[roffs + 8] = xstrdup(rowpp[m * nncols + k]);
s->rows[roffs + 9] = xstrdup("A");
}
} else if (strcmp(rowpp[k], "seqno") == 0) {
int m;
for (m = 1; m <= nnrows; m++) {
int roffs = (offs + addipk + m) * s->ncols;
int pos = m - 1;
char buf[32];
sscanf(rowpp[m * nncols + k], "%d", &pos);
sprintf(buf, "%d", pos + 1);
s->rows[roffs + 7] = xstrdup(buf);
}
}
}
offs += nnrows;
sqlite_free_table(rowpp);
}
}
sqlite_free_table(rowp);
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Return statistic information on table indices.
* @param stmt statement handle
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param itype type of index information
* @param resv reserved
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLStatistics(SQLHSTMT stmt, SQLCHAR *cat, SQLSMALLINT catLen,
SQLCHAR *schema, SQLSMALLINT schemaLen,
SQLCHAR *table, SQLSMALLINT tableLen,
SQLUSMALLINT itype, SQLUSMALLINT resv)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvstatistics(stmt, cat, catLen, schema, schemaLen,
table, tableLen, itype, resv);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Return statistic information on table indices (UNICODE version).
* @param stmt statement handle
* @param cat catalog name/pattern or NULL
* @param catLen length of catalog name/pattern or SQL_NTS
* @param schema schema name/pattern or NULL
* @param schemaLen length of schema name/pattern or SQL_NTS
* @param table table name/pattern or NULL
* @param tableLen length of table name/pattern or SQL_NTS
* @param itype type of index information
* @param resv reserved
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLStatisticsW(SQLHSTMT stmt, SQLWCHAR *cat, SQLSMALLINT catLen,
SQLWCHAR *schema, SQLSMALLINT schemaLen,
SQLWCHAR *table, SQLSMALLINT tableLen,
SQLUSMALLINT itype, SQLUSMALLINT resv)
{
char *c = NULL, *s = NULL, *t = NULL;
SQLRETURN ret;
HSTMT_LOCK(stmt);
if (cat) {
c = uc_to_utf_c(cat, catLen);
if (!c) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (schema) {
s = uc_to_utf_c(schema, schemaLen);
if (!s) {
ret = nomem((STMT *) stmt);
goto done;
}
}
if (table) {
t = uc_to_utf_c(table, tableLen);
if (!t) {
ret = nomem((STMT *) stmt);
goto done;
}
}
ret = drvstatistics(stmt, (SQLCHAR *) c, SQL_NTS, (SQLCHAR *) s, SQL_NTS,
(SQLCHAR *) t, SQL_NTS, itype, resv);
done:
HSTMT_UNLOCK(stmt);
uc_free(t);
uc_free(s);
uc_free(c);
return ret;
}
#endif
/**
* Retrieve row data after fetch.
* @param stmt statement handle
* @param col column number, starting at 1
* @param type output type
* @param val output buffer
* @param len length of output buffer
* @param lenp output length
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLGetData(SQLHSTMT stmt, SQLUSMALLINT col, SQLSMALLINT type,
SQLPOINTER val, SQLLEN len, SQLLEN *lenp)
{
STMT *s;
SQLRETURN ret = SQL_ERROR;
HSTMT_LOCK(stmt);
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (col == 0 && s->bkmrk && type == SQL_C_BOOKMARK) {
*((long *) val) = s->rowp;
if (lenp) {
*lenp = sizeof (long);
}
ret = SQL_SUCCESS;
goto done;
}
if (col < 1 || col > s->ncols) {
setstat(s, -1, "invalid column", (*s->ov3) ? "07009" : "S1002");
goto done;
}
--col;
ret = getrowdata(s, col, type, val, len, lenp, 1);
done:
HSTMT_UNLOCK(stmt);
return ret;
}
/**
* Internal: fetch and bind from statement's current row
* @param s statement pointer
* @param rsi rowset index
* @result ODBC error code
*/
static SQLRETURN
dofetchbind(STMT *s, int rsi)
{
int ret, i, withinfo = 0;
s->row_status0[rsi] = SQL_ROW_SUCCESS;
if (s->bkmrk && s->bkmrkcol.valp) {
long *val;
if (s->bind_type != SQL_BIND_BY_COLUMN) {
val = (long *) ((char *) s->bkmrkcol.valp + s->bind_type * rsi);
} else {
val = (long *) s->bkmrkcol.valp + rsi;
}
if (s->bind_offs) {
val = (long *) ((char *) val + *s->bind_offs);
}
*val = s->rowp;
if (s->bkmrkcol.lenp) {
SQLLEN *ival;
if (s->bind_type != SQL_BIND_BY_COLUMN) {
ival = (SQLLEN *) ((char *) s->bkmrkcol.lenp +
s->bind_type * rsi);
} else {
ival = &s->bkmrkcol.lenp[rsi];
}
if (s->bind_offs) {
ival = (SQLLEN *) ((char *) ival + *s->bind_offs);
}
*ival = sizeof (long);
}
}
ret = SQL_SUCCESS;
for (i = 0; s->bindcols && i < s->ncols; i++) {
BINDCOL *b = &s->bindcols[i];
SQLPOINTER dp = 0;
SQLLEN *lp = 0;
b->offs = 0;
if (b->valp) {
if (s->bind_type != SQL_BIND_BY_COLUMN) {
dp = (SQLPOINTER) ((char *) b->valp + s->bind_type * rsi);
} else {
dp = (SQLPOINTER) ((char *) b->valp + b->max * rsi);
}
if (s->bind_offs) {
dp = (SQLPOINTER) ((char *) dp + *s->bind_offs);
}
}
if (b->lenp) {
if (s->bind_type != SQL_BIND_BY_COLUMN) {
lp = (SQLLEN *) ((char *) b->lenp + s->bind_type * rsi);
} else {
lp = b->lenp + rsi;
}
if (s->bind_offs) {
lp = (SQLLEN *) ((char *) lp + *s->bind_offs);
}
}
if (dp || lp) {
ret = getrowdata(s, (SQLUSMALLINT) i, b->type, dp, b->max, lp, 0);
if (!SQL_SUCCEEDED(ret)) {
s->row_status0[rsi] = SQL_ROW_ERROR;
break;
}
if (ret != SQL_SUCCESS) {
withinfo = 1;
#ifdef SQL_ROW_SUCCESS_WITH_INFO
s->row_status0[rsi] = SQL_ROW_SUCCESS_WITH_INFO;
#endif
}
}
}
if (SQL_SUCCEEDED(ret)) {
ret = withinfo ? SQL_SUCCESS_WITH_INFO : SQL_SUCCESS;
}
return ret;
}
/**
* Internal fetch function for SQLFetchScroll() and SQLExtendedFetch().
* @param stmt statement handle
* @param orient fetch direction
* @param offset offset for fetch direction
* @result ODBC error code
*/
static SQLRETURN
drvfetchscroll(SQLHSTMT stmt, SQLSMALLINT orient, SQLINTEGER offset)
{
STMT *s;
int i, withinfo = 0;
SQLRETURN ret;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
for (i = 0; i < s->rowset_size; i++) {
s->row_status0[i] = SQL_ROW_NOROW;
}
if (s->row_status) {
memcpy(s->row_status, s->row_status0,
sizeof (SQLUSMALLINT) * s->rowset_size);
}
s->row_count0 = 0;
if (s->row_count) {
*s->row_count = s->row_count0;
}
if (!s->bindcols) {
for (i = 0; i < s->rowset_size; i++) {
s->row_status0[i] = SQL_ROW_ERROR;
}
ret = SQL_ERROR;
i = 0;
goto done2;
}
if (!s->isselect) {
setstat(s, -1, "no result set available", "24000");
ret = SQL_ERROR;
i = s->nrows;
goto done2;
}
if (s->curtype == SQL_CURSOR_FORWARD_ONLY && orient != SQL_FETCH_NEXT) {
setstat(s, -1, "wrong fetch direction", "01000");
ret = SQL_ERROR;
i = 0;
goto done2;
}
ret = SQL_SUCCESS;
i = 0;
if (((DBC *) (s->dbc))->vm_stmt == s && s->vm) {
s->rowp = 0;
for (; i < s->rowset_size; i++) {
ret = vm_step(s);
if (ret != SQL_SUCCESS) {
s->row_status0[i] = SQL_ROW_ERROR;
break;
}
if (s->nrows < 1) {
break;
}
ret = dofetchbind(s, i);
if (!SQL_SUCCEEDED(ret)) {
break;
} else if (ret == SQL_SUCCESS_WITH_INFO) {
withinfo = 1;
}
}
} else if (s->rows) {
switch (orient) {
case SQL_FETCH_NEXT:
if (s->nrows < 1) {
return SQL_NO_DATA;
}
if (s->rowp < 0) {
s->rowp = -1;
}
if (s->rowp >= s->nrows) {
s->rowp = s->nrows;
return SQL_NO_DATA;
}
break;
case SQL_FETCH_PRIOR:
if (s->nrows < 1) {
s->rowp = -1;
return SQL_NO_DATA;
}
s->rowp -= s->rowset_size + 1;
if (s->rowp < -1) {
s->rowp = -1;
return SQL_NO_DATA;
}
break;
case SQL_FETCH_FIRST:
if (s->nrows < 1) {
return SQL_NO_DATA;
}
s->rowp = -1;
break;
case SQL_FETCH_LAST:
if (s->nrows < 1) {
return SQL_NO_DATA;
}
s->rowp = s->nrows - s->rowset_size;
if (--s->rowp < -1) {
s->rowp = -1;
}
break;
case SQL_FETCH_ABSOLUTE:
if (offset == 0) {
s->rowp = -1;
return SQL_NO_DATA;
} else if (offset < 0) {
if (0 - offset <= s->nrows) {
s->rowp = s->nrows + offset - 1;
break;
}
s->rowp = -1;
return SQL_NO_DATA;
} else if (offset > s->nrows) {
s->rowp = s->nrows;
return SQL_NO_DATA;
}
s->rowp = offset - 1 - 1;
break;
case SQL_FETCH_RELATIVE:
if (offset >= 0) {
s->rowp += offset * s->rowset_size - 1;
if (s->rowp >= s->nrows) {
s->rowp = s->nrows;
return SQL_NO_DATA;
}
} else {
s->rowp += offset * s->rowset_size - 1;
if (s->rowp < -1) {
s->rowp = -1;
return SQL_NO_DATA;
}
}
break;
case SQL_FETCH_BOOKMARK:
if (s->bkmrk) {
if (offset < 0 || offset >= s->nrows) {
return SQL_NO_DATA;
}
s->rowp = offset - 1;
break;
}
/* fall through */
default:
s->row_status0[0] = SQL_ROW_ERROR;
ret = SQL_ERROR;
goto done;
}
for (; i < s->rowset_size; i++) {
++s->rowp;
if (s->rowp < 0 || s->rowp >= s->nrows) {
break;
}
ret = dofetchbind(s, i);
if (!SQL_SUCCEEDED(ret)) {
break;
} else if (ret == SQL_SUCCESS_WITH_INFO) {
withinfo = 1;
}
}
}
done:
if (i == 0) {
if (SQL_SUCCEEDED(ret)) {
return SQL_NO_DATA;
}
return ret;
}
if (SQL_SUCCEEDED(ret)) {
ret = withinfo ? SQL_SUCCESS_WITH_INFO : SQL_SUCCESS;
}
done2:
if (s->row_status) {
memcpy(s->row_status, s->row_status0,
sizeof (SQLUSMALLINT) * s->rowset_size);
}
s->row_count0 = i;
if (s->row_count) {
*s->row_count = s->row_count0;
}
return ret;
}
/**
* Fetch next result row.
* @param stmt statement handle
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLFetch(SQLHSTMT stmt)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvfetchscroll(stmt, SQL_FETCH_NEXT, 0);
HSTMT_UNLOCK(stmt);
return ret;
}
/**
* Fetch result row with scrolling.
* @param stmt statement handle
* @param orient fetch direction
* @param offset offset for fetch direction
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLFetchScroll(SQLHSTMT stmt, SQLSMALLINT orient, SQLLEN offset)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvfetchscroll(stmt, orient, offset);
HSTMT_UNLOCK(stmt);
return ret;
}
/**
* Fetch result row with scrolling and row status.
* @param stmt statement handle
* @param orient fetch direction
* @param offset offset for fetch direction
* @param rowcount output number of fetched rows
* @param rowstatus array for row stati
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLExtendedFetch(SQLHSTMT stmt, SQLUSMALLINT orient, SQLROWOFFSET offset,
SQLROWSETSIZE *rowcount, SQLUSMALLINT *rowstatus)
{
STMT *s;
SQLRETURN ret;
SQLUSMALLINT *rst;
HSTMT_LOCK(stmt);
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
/* temporarily turn off SQL_ATTR_ROW_STATUS_PTR */
rst = s->row_status;
s->row_status = 0;
ret = drvfetchscroll(stmt, orient, offset);
s->row_status = rst;
if (rowstatus) {
memcpy(rowstatus, s->row_status0,
sizeof (SQLUSMALLINT) * s->rowset_size);
}
if (rowcount) {
*rowcount = s->row_count0;
}
HSTMT_UNLOCK(stmt);
return ret;
}
/**
* Return number of affected rows of HSTMT.
* @param stmt statement handle
* @param nrows output number of rows
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLRowCount(SQLHSTMT stmt, SQLLEN *nrows)
{
STMT *s;
HSTMT_LOCK(stmt);
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (nrows) {
*nrows = s->nrows;
}
HSTMT_UNLOCK(stmt);
return SQL_SUCCESS;
}
/**
* Return number of columns of result set given HSTMT.
* @param stmt statement handle
* @param ncols output number of columns
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLNumResultCols(SQLHSTMT stmt, SQLSMALLINT *ncols)
{
STMT *s;
HSTMT_LOCK(stmt);
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (ncols) {
*ncols = s->ncols;
}
HSTMT_UNLOCK(stmt);
return SQL_SUCCESS;
}
/**
* Internal describe column information.
* @param stmt statement handle
* @param col column number, starting at 1
* @param name buffer for column name
* @param nameMax length of name buffer
* @param nameLen output length of column name
* @param type output SQL type
* @param size output column size
* @param digits output number of digits
* @param nullable output NULL allowed indicator
* @result ODBC error code
*/
static SQLRETURN
drvdescribecol(SQLHSTMT stmt, SQLUSMALLINT col, SQLCHAR *name,
SQLSMALLINT nameMax, SQLSMALLINT *nameLen,
SQLSMALLINT *type, SQLULEN *size,
SQLSMALLINT *digits, SQLSMALLINT *nullable)
{
STMT *s;
COL *c;
int didname = 0;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (!s->cols) {
setstat(s, -1, "no columns", (*s->ov3) ? "07009" : "S1002");
return SQL_ERROR;
}
if (col < 1 || col > s->ncols) {
setstat(s, -1, "invalid column", (*s->ov3) ? "07009" : "S1002");
return SQL_ERROR;
}
c = s->cols + col - 1;
if (name && nameMax > 0) {
strncpy((char *) name, c->column, nameMax);
name[nameMax - 1] = '\0';
didname = 1;
}
if (nameLen) {
if (didname) {
*nameLen = strlen((char *) name);
} else {
*nameLen = strlen(c->column);
}
}
if (type) {
*type = c->type;
#ifdef WINTERFACE
if (s->nowchar[0] || s->nowchar[1]) {
switch (c->type) {
case SQL_WCHAR:
*type = SQL_CHAR;
break;
case SQL_WVARCHAR:
*type = SQL_VARCHAR;
break;
#ifdef SQL_LONGVARCHAR
case SQL_WLONGVARCHAR:
*type = SQL_LONGVARCHAR;
break;
#endif
}
}
#endif
}
if (size) {
*size = c->size;
}
if (digits) {
*digits = 0;
}
if (nullable) {
*nullable = 1;
}
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Describe column information.
* @param stmt statement handle
* @param col column number, starting at 1
* @param name buffer for column name
* @param nameMax length of name buffer
* @param nameLen output length of column name
* @param type output SQL type
* @param size output column size
* @param digits output number of digits
* @param nullable output NULL allowed indicator
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLDescribeCol(SQLHSTMT stmt, SQLUSMALLINT col, SQLCHAR *name,
SQLSMALLINT nameMax, SQLSMALLINT *nameLen,
SQLSMALLINT *type, SQLULEN *size,
SQLSMALLINT *digits, SQLSMALLINT *nullable)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvdescribecol(stmt, col, name, nameMax, nameLen,
type, size, digits, nullable);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Describe column information (UNICODE version).
* @param stmt statement handle
* @param col column number, starting at 1
* @param name buffer for column name
* @param nameMax length of name buffer
* @param nameLen output length of column name
* @param type output SQL type
* @param size output column size
* @param digits output number of digits
* @param nullable output NULL allowed indicator
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLDescribeColW(SQLHSTMT stmt, SQLUSMALLINT col, SQLWCHAR *name,
SQLSMALLINT nameMax, SQLSMALLINT *nameLen,
SQLSMALLINT *type, SQLULEN *size,
SQLSMALLINT *digits, SQLSMALLINT *nullable)
{
SQLRETURN ret;
SQLSMALLINT len = 0;
HSTMT_LOCK(stmt);
ret = drvdescribecol(stmt, col, (SQLCHAR *) name,
(SQLSMALLINT) (nameMax * sizeof (SQLWCHAR)),
&len, type, size, digits, nullable);
if (ret == SQL_SUCCESS) {
if (name) {
if (len > 0) {
SQLWCHAR *n = NULL;
n = uc_from_utf((SQLCHAR *) name, len);
if (n) {
uc_strncpy(name, n, nameMax);
n[len] = 0;
len = min(nameMax, uc_strlen(n));
uc_free(n);
} else {
len = 0;
}
}
if (len <= 0) {
len = 0;
if (nameMax > 0) {
name[0] = 0;
}
}
} else {
STMT *s = (STMT *) stmt;
COL *c = s->cols + col - 1;
len = 0;
if (c->column) {
len = strlen(c->column);
}
}
if (nameLen) {
*nameLen = len;
}
}
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
/**
* Internal retrieve column attributes.
* @param stmt statement handle
* @param col column number, starting at 1
* @param id attribute id
* @param val output buffer
* @param valMax length of output buffer
* @param valLen output length
* @param val2 integer output buffer
* @result ODBC error code
*/
static SQLRETURN
drvcolattributes(SQLHSTMT stmt, SQLUSMALLINT col, SQLUSMALLINT id,
SQLPOINTER val, SQLSMALLINT valMax, SQLSMALLINT *valLen,
SQLLEN *val2)
{
STMT *s;
COL *c;
SQLSMALLINT dummy;
char *valc = (char *) val;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (!s->cols) {
return SQL_ERROR;
}
if (!valLen) {
valLen = &dummy;
}
if (id == SQL_COLUMN_COUNT) {
if (val2) {
*val2 = s->ncols;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
}
if (id == SQL_COLUMN_TYPE && col == 0) {
if (val2) {
*val2 = SQL_INTEGER;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
}
#ifdef SQL_DESC_OCTET_LENGTH
if (id == SQL_DESC_OCTET_LENGTH && col == 0) {
if (val2) {
*val2 = 4;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
}
#endif
if (col < 1 || col > s->ncols) {
setstat(s, -1, "invalid column", (*s->ov3) ? "07009": "S1002");
return SQL_ERROR;
}
c = s->cols + col - 1;
switch (id) {
case SQL_COLUMN_LABEL:
if (c->label) {
if (valc && valMax > 0) {
strncpy(valc, c->label, valMax);
valc[valMax - 1] = '\0';
}
*valLen = strlen(c->label);
goto checkLen;
}
/* fall through */
case SQL_COLUMN_NAME:
case SQL_DESC_NAME:
if (valc && valMax > 0) {
strncpy(valc, c->column, valMax);
valc[valMax - 1] = '\0';
}
*valLen = strlen(c->column);
checkLen:
if (*valLen >= valMax) {
setstat(s, -1, "data right truncated", "01004");
return SQL_SUCCESS_WITH_INFO;
}
return SQL_SUCCESS;
#ifdef SQL_DESC_BASE_COLUMN_NAME
case SQL_DESC_BASE_COLUMN_NAME:
if (strchr(c->column, '(') || strchr(c->column, ')')) {
valc[0] = '\0';
*valLen = 0;
} else if (valc && valMax > 0) {
strncpy(valc, c->column, valMax);
valc[valMax - 1] = '\0';
*valLen = strlen(c->column);
}
goto checkLen;
#endif
case SQL_COLUMN_TYPE:
case SQL_DESC_TYPE:
#ifdef WINTERFACE
{
int type = c->type;
if (s->nowchar[0] || s->nowchar[1]) {
switch (type) {
case SQL_WCHAR:
type = SQL_CHAR;
break;
case SQL_WVARCHAR:
type = SQL_VARCHAR;
break;
#ifdef SQL_LONGVARCHAR
case SQL_WLONGVARCHAR:
type = SQL_LONGVARCHAR;
break;
}
}
if (val2) {
*val2 = type;
}
#endif
}
#else
if (val2) {
*val2 = c->type;
}
#endif
*valLen = sizeof (int);
return SQL_SUCCESS;
case SQL_COLUMN_DISPLAY_SIZE:
if (val2) {
*val2 = c->size;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
case SQL_COLUMN_UNSIGNED:
if (val2) {
*val2 = c->nosign ? SQL_TRUE : SQL_FALSE;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
case SQL_COLUMN_SCALE:
case SQL_DESC_SCALE:
if (c->type == SQL_TIMESTAMP) {
if (val2) {
*val2 = 3;
}
}
#ifdef SQL_TYPE_TIMESTAMP
else if (c->type == SQL_TYPE_TIMESTAMP) {
if (val2) {
*val2 = 3;
}
}
#endif
else if (val2) {
*val2 = c->scale;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
case SQL_COLUMN_PRECISION:
case SQL_DESC_PRECISION:
if (val2) {
switch (c->type) {
case SQL_SMALLINT:
*val2 = 5;
break;
case SQL_INTEGER:
*val2 = 10;
break;
case SQL_FLOAT:
case SQL_REAL:
case SQL_DOUBLE:
*val2 = 15;
break;
case SQL_DATE:
*val2 = 10;
break;
case SQL_TIME:
*val2 = 8;
break;
#ifdef SQL_TYPE_TIMESTAMP
case SQL_TYPE_TIMESTAMP:
#endif
case SQL_TIMESTAMP:
*val2 = 23;
break;
default:
*val2 = c->prec;
break;
}
}
*valLen = sizeof (int);
return SQL_SUCCESS;
case SQL_COLUMN_MONEY:
if (val2) {
*val2 = SQL_FALSE;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
case SQL_COLUMN_AUTO_INCREMENT:
if (val2) {
*val2 = c->autoinc;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
case SQL_COLUMN_LENGTH:
case SQL_DESC_LENGTH:
if (val2) {
*val2 = c->size;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
case SQL_COLUMN_NULLABLE:
case SQL_DESC_NULLABLE:
if (val2) {
*val2 = c->notnull;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
case SQL_COLUMN_SEARCHABLE:
if (val2) {
*val2 = SQL_SEARCHABLE;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
case SQL_COLUMN_CASE_SENSITIVE:
if (val2) {
*val2 = SQL_TRUE;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
case SQL_COLUMN_UPDATABLE:
if (val2) {
*val2 = SQL_TRUE;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
case SQL_DESC_COUNT:
if (val2) {
*val2 = s->ncols;
}
*valLen = sizeof (int);
return SQL_SUCCESS;
case SQL_COLUMN_TYPE_NAME: {
char *p = NULL, *tn = c->typename ? c->typename : "varchar";
#ifdef WINTERFACE
if (c->type == SQL_WCHAR ||
c->type == SQL_WVARCHAR ||
c->type == SQL_WLONGVARCHAR) {
if (!(s->nowchar[0] || s->nowchar[1])) {
if (strcasecmp(tn, "varchar") == 0) {
tn = "wvarchar";
}
}
}
#endif
if (valc && valMax > 0) {
strncpy(valc, tn, valMax);
valc[valMax - 1] = '\0';
p = strchr(valc, '(');
if (p) {
*p = '\0';
while (p > valc && ISSPACE(p[-1])) {
--p;
*p = '\0';
}
}
*valLen = strlen(valc);
} else {
*valLen = strlen(tn);
p = strchr(tn, '(');
if (p) {
*valLen = p - tn;
while (p > tn && ISSPACE(p[-1])) {
--p;
*valLen -= 1;
}
}
}
goto checkLen;
}
case SQL_COLUMN_OWNER_NAME:
case SQL_COLUMN_QUALIFIER_NAME: {
char *z = "";
if (valc && valMax > 0) {
strncpy(valc, z, valMax);
valc[valMax - 1] = '\0';
}
*valLen = strlen(z);
goto checkLen;
}
case SQL_COLUMN_TABLE_NAME:
#if (SQL_COLUMN_TABLE_NAME != SQL_DESC_TABLE_NAME)
case SQL_DESC_TABLE_NAME:
#endif
#ifdef SQL_DESC_BASE_TABLE_NAME
case SQL_DESC_BASE_TABLE_NAME:
#endif
if (valc && valMax > 0) {
strncpy(valc, c->table, valMax);
valc[valMax - 1] = '\0';
}
*valLen = strlen(c->table);
goto checkLen;
#ifdef SQL_DESC_NUM_PREC_RADIX
case SQL_DESC_NUM_PREC_RADIX:
if (val2) {
switch (c->type) {
#ifdef WINTERFACE
case SQL_WCHAR:
case SQL_WVARCHAR:
#ifdef SQL_LONGVARCHAR
case SQL_WLONGVARCHAR:
#endif
#endif
case SQL_CHAR:
case SQL_VARCHAR:
#ifdef SQL_LONGVARCHAR
case SQL_LONGVARCHAR:
#endif
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
*val2 = 0;
break;
default:
*val2 = 2;
}
}
*valLen = sizeof (int);
return SQL_SUCCESS;
#endif
}
setstat(s, -1, "unsupported column attributes %d", "HY091", id);
return SQL_ERROR;
}
#ifndef WINTERFACE
/**
* Retrieve column attributes.
* @param stmt statement handle
* @param col column number, starting at 1
* @param id attribute id
* @param val output buffer
* @param valMax length of output buffer
* @param valLen output length
* @param val2 integer output buffer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLColAttributes(SQLHSTMT stmt, SQLUSMALLINT col, SQLUSMALLINT id,
SQLPOINTER val, SQLSMALLINT valMax, SQLSMALLINT *valLen,
SQLLEN *val2)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvcolattributes(stmt, col, id, val, valMax, valLen, val2);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Retrieve column attributes (UNICODE version).
* @param stmt statement handle
* @param col column number, starting at 1
* @param id attribute id
* @param val output buffer
* @param valMax length of output buffer
* @param valLen output length
* @param val2 integer output buffer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLColAttributesW(SQLHSTMT stmt, SQLUSMALLINT col, SQLUSMALLINT id,
SQLPOINTER val, SQLSMALLINT valMax, SQLSMALLINT *valLen,
SQLLEN *val2)
{
SQLRETURN ret;
SQLSMALLINT len = 0;
HSTMT_LOCK(stmt);
ret = drvcolattributes(stmt, col, id, val, valMax, &len, val2);
if (SQL_SUCCEEDED(ret)) {
SQLWCHAR *v = NULL;
switch (id) {
case SQL_COLUMN_LABEL:
case SQL_COLUMN_NAME:
case SQL_DESC_NAME:
case SQL_COLUMN_TYPE_NAME:
case SQL_COLUMN_OWNER_NAME:
case SQL_COLUMN_QUALIFIER_NAME:
case SQL_COLUMN_TABLE_NAME:
#if (SQL_COLUMN_TABLE_NAME != SQL_DESC_TABLE_NAME)
case SQL_DESC_TABLE_NAME:
#endif
#ifdef SQL_DESC_BASE_COLUMN_NAME
case SQL_DESC_BASE_COLUMN_NAME:
#endif
#ifdef SQL_DESC_BASE_TABLE_NAME
case SQL_DESC_BASE_TABLE_NAME:
#endif
if (val && valMax > 0) {
int vmax = valMax / sizeof (SQLWCHAR);
v = uc_from_utf((SQLCHAR *) val, SQL_NTS);
if (v) {
uc_strncpy(val, v, vmax);
len = min(vmax, uc_strlen(v));
uc_free(v);
len *= sizeof (SQLWCHAR);
}
if (vmax > 0) {
v = (SQLWCHAR *) val;
v[vmax - 1] = '\0';
}
}
if (len <= 0) {
len = 0;
}
break;
}
if (valLen) {
*valLen = len;
}
}
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
/**
* Internal retrieve column attributes.
* @param stmt statement handle
* @param col column number, starting at 1
* @param id attribute id
* @param val output buffer
* @param valMax length of output buffer
* @param valLen output length
* @param val2 integer output buffer
* @result ODBC error code
*/
static SQLRETURN
drvcolattribute(SQLHSTMT stmt, SQLUSMALLINT col, SQLUSMALLINT id,
SQLPOINTER val, SQLSMALLINT valMax, SQLSMALLINT *valLen,
SQLPOINTER val2)
{
STMT *s;
COL *c;
int v = 0;
char *valc = (char *) val;
SQLSMALLINT dummy;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (!s->cols) {
return SQL_ERROR;
}
if (col < 1 || col > s->ncols) {
setstat(s, -1, "invalid column", (*s->ov3) ? "07009" : "S1002");
return SQL_ERROR;
}
if (!valLen) {
valLen = &dummy;
}
c = s->cols + col - 1;
switch (id) {
case SQL_DESC_COUNT:
v = s->ncols;
break;
case SQL_DESC_CATALOG_NAME:
if (valc && valMax > 0) {
strncpy(valc, c->db, valMax);
valc[valMax - 1] = '\0';
}
*valLen = strlen(c->db);
checkLen:
if (*valLen >= valMax) {
setstat(s, -1, "data right truncated", "01004");
return SQL_SUCCESS_WITH_INFO;
}
break;
case SQL_COLUMN_LENGTH:
case SQL_DESC_LENGTH:
v = c->size;
break;
case SQL_COLUMN_LABEL:
if (c->label) {
if (valc && valMax > 0) {
strncpy(valc, c->label, valMax);
valc[valMax - 1] = '\0';
}
*valLen = strlen(c->label);
goto checkLen;
}
/* fall through */
case SQL_COLUMN_NAME:
case SQL_DESC_NAME:
if (valc && valMax > 0) {
strncpy(valc, c->column, valMax);
valc[valMax - 1] = '\0';
}
*valLen = strlen(c->column);
goto checkLen;
case SQL_DESC_SCHEMA_NAME: {
char *z = "";
if (valc && valMax > 0) {
strncpy(valc, z, valMax);
valc[valMax - 1] = '\0';
}
*valLen = strlen(z);
goto checkLen;
}
#ifdef SQL_DESC_BASE_COLUMN_NAME
case SQL_DESC_BASE_COLUMN_NAME:
if (strchr(c->column, '(') || strchr(c->column, ')')) {
valc[0] = '\0';
*valLen = 0;
} else if (valc && valMax > 0) {
strncpy(valc, c->column, valMax);
valc[valMax - 1] = '\0';
*valLen = strlen(c->column);
}
goto checkLen;
#endif
case SQL_DESC_TYPE_NAME: {
char *p = NULL, *tn = c->typename ? c->typename : "varchar";
#ifdef WINTERFACE
if (c->type == SQL_WCHAR ||
c->type == SQL_WVARCHAR ||
c->type == SQL_WLONGVARCHAR) {
if (!(s->nowchar[0] || s->nowchar[1])) {
if (strcasecmp(tn, "varchar") == 0) {
tn = "wvarchar";
}
}
}
#endif
if (valc && valMax > 0) {
strncpy(valc, tn, valMax);
valc[valMax - 1] = '\0';
p = strchr(valc, '(');
if (p) {
*p = '\0';
while (p > valc && ISSPACE(p[-1])) {
--p;
*p = '\0';
}
}
*valLen = strlen(valc);
} else {
*valLen = strlen(tn);
p = strchr(tn, '(');
if (p) {
*valLen = p - tn;
while (p > tn && ISSPACE(p[-1])) {
--p;
*valLen -= 1;
}
}
}
goto checkLen;
}
case SQL_DESC_OCTET_LENGTH:
v = c->size;
#ifdef WINTERFACE
if (c->type == SQL_WCHAR ||
c->type == SQL_WVARCHAR ||
c->type == SQL_WLONGVARCHAR) {
if (!(s->nowchar[0] || s->nowchar[1])) {
v *= sizeof (SQLWCHAR);
}
}
#endif
break;
#if (SQL_COLUMN_TABLE_NAME != SQL_DESC_TABLE_NAME)
case SQL_COLUMN_TABLE_NAME:
#endif
case SQL_DESC_TABLE_NAME:
#ifdef SQL_DESC_BASE_TABLE_NAME
case SQL_DESC_BASE_TABLE_NAME:
#endif
if (valc && valMax > 0) {
strncpy(valc, c->table, valMax);
valc[valMax - 1] = '\0';
}
*valLen = strlen(c->table);
goto checkLen;
case SQL_DESC_TYPE:
v = c->type;
#ifdef WINTERFACE
if (s->nowchar[0] || s->nowchar[1]) {
switch (v) {
case SQL_WCHAR:
v = SQL_CHAR;
break;
case SQL_WVARCHAR:
v = SQL_VARCHAR;
break;
#ifdef SQL_LONGVARCHAR
case SQL_WLONGVARCHAR:
v = SQL_LONGVARCHAR;
break;
#endif
}
}
#endif
break;
case SQL_DESC_CONCISE_TYPE:
switch (c->type) {
case SQL_INTEGER:
v = SQL_C_LONG;
break;
case SQL_TINYINT:
v = SQL_C_TINYINT;
break;
case SQL_SMALLINT:
v = SQL_C_SHORT;
break;
case SQL_FLOAT:
v = SQL_C_FLOAT;
break;
case SQL_DOUBLE:
v = SQL_C_DOUBLE;
break;
case SQL_TIMESTAMP:
v = SQL_C_TIMESTAMP;
break;
case SQL_TIME:
v = SQL_C_TIME;
break;
case SQL_DATE:
v = SQL_C_DATE;
break;
#ifdef SQL_C_TYPE_TIMESTAMP
case SQL_TYPE_TIMESTAMP:
v = SQL_C_TYPE_TIMESTAMP;
break;
#endif
#ifdef SQL_C_TYPE_TIME
case SQL_TYPE_TIME:
v = SQL_C_TYPE_TIME;
break;
#endif
#ifdef SQL_C_TYPE_DATE
case SQL_TYPE_DATE:
v = SQL_C_TYPE_DATE;
break;
#endif
#ifdef SQL_BIT
case SQL_BIT:
v = SQL_C_BIT;
break;
#endif
default:
#ifdef WINTERFACE
v = (s->nowchar[0] || s->nowchar[1]) ? SQL_C_CHAR : SQL_C_WCHAR;
#else
v = SQL_C_CHAR;
#endif
break;
}
break;
case SQL_DESC_UPDATABLE:
v = SQL_TRUE;
break;
case SQL_COLUMN_DISPLAY_SIZE:
v = c->size;
break;
case SQL_COLUMN_UNSIGNED:
v = c->nosign ? SQL_TRUE : SQL_FALSE;
break;
case SQL_COLUMN_SEARCHABLE:
v = SQL_SEARCHABLE;
break;
case SQL_COLUMN_SCALE:
case SQL_DESC_SCALE:
if (c->type == SQL_TIMESTAMP) {
v = 3;
}
#ifdef SQL_TYPE_TIMESTAMP
else if (c->type == SQL_TYPE_TIMESTAMP) {
v = 3;
}
#endif
else {
v = c->scale;
}
break;
case SQL_COLUMN_PRECISION:
case SQL_DESC_PRECISION:
switch (c->type) {
case SQL_SMALLINT:
v = 5;
break;
case SQL_INTEGER:
v = 10;
break;
case SQL_FLOAT:
case SQL_REAL:
case SQL_DOUBLE:
v = 15;
break;
case SQL_DATE:
v = 10;
break;
case SQL_TIME:
v = 8;
break;
#ifdef SQL_TYPE_TIMESTAMP
case SQL_TYPE_TIMESTAMP:
#endif
case SQL_TIMESTAMP:
v = 23;
break;
default:
v = c->prec;
break;
}
break;
case SQL_COLUMN_MONEY:
v = SQL_FALSE;
break;
case SQL_COLUMN_AUTO_INCREMENT:
v = c->autoinc;
break;
case SQL_DESC_NULLABLE:
v = c->notnull;
break;
#ifdef SQL_DESC_NUM_PREC_RADIX
case SQL_DESC_NUM_PREC_RADIX:
switch (c->type) {
#ifdef WINTERFACE
case SQL_WCHAR:
case SQL_WVARCHAR:
#ifdef SQL_LONGVARCHAR
case SQL_WLONGVARCHAR:
#endif
#endif
case SQL_CHAR:
case SQL_VARCHAR:
#ifdef SQL_LONGVARCHAR
case SQL_LONGVARCHAR:
#endif
case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
v = 0;
break;
default:
v = 2;
}
break;
#endif
default:
setstat(s, -1, "unsupported column attribute %d", "HY091", id);
return SQL_ERROR;
}
if (val2) {
*(int *) val2 = v;
}
return SQL_SUCCESS;
}
#ifndef WINTERFACE
/**
* Retrieve column attributes.
* @param stmt statement handle
* @param col column number, starting at 1
* @param id attribute id
* @param val output buffer
* @param valMax length of output buffer
* @param valLen output length
* @param val2 integer output buffer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLColAttribute(SQLHSTMT stmt, SQLUSMALLINT col, SQLUSMALLINT id,
SQLPOINTER val, SQLSMALLINT valMax, SQLSMALLINT *valLen,
COLATTRIBUTE_LAST_ARG_TYPE val2)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvcolattribute(stmt, col, id, val, valMax, valLen,
(SQLPOINTER) val2);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Retrieve column attributes (UNICODE version).
* @param stmt statement handle
* @param col column number, starting at 1
* @param id attribute id
* @param val output buffer
* @param valMax length of output buffer
* @param valLen output length
* @param val2 integer output buffer
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLColAttributeW(SQLHSTMT stmt, SQLUSMALLINT col, SQLUSMALLINT id,
SQLPOINTER val, SQLSMALLINT valMax, SQLSMALLINT *valLen,
COLATTRIBUTE_LAST_ARG_TYPE val2)
{
SQLRETURN ret;
SQLSMALLINT len = 0;
HSTMT_LOCK(stmt);
ret = drvcolattribute(stmt, col, id, val, valMax, &len,
(SQLPOINTER) val2);
if (SQL_SUCCEEDED(ret)) {
SQLWCHAR *v = NULL;
switch (id) {
case SQL_DESC_SCHEMA_NAME:
case SQL_DESC_CATALOG_NAME:
case SQL_COLUMN_LABEL:
case SQL_DESC_NAME:
case SQL_DESC_TABLE_NAME:
#ifdef SQL_DESC_BASE_TABLE_NAME
case SQL_DESC_BASE_TABLE_NAME:
#endif
#ifdef SQL_DESC_BASE_COLUMN_NAME
case SQL_DESC_BASE_COLUMN_NAME:
#endif
case SQL_DESC_TYPE_NAME:
if (val && valMax > 0) {
int vmax = valMax / sizeof (SQLWCHAR);
v = uc_from_utf((SQLCHAR *) val, SQL_NTS);
if (v) {
uc_strncpy(val, v, vmax);
len = min(vmax, uc_strlen(v));
uc_free(v);
len *= sizeof (SQLWCHAR);
}
if (vmax > 0) {
v = (SQLWCHAR *) val;
v[vmax - 1] = '\0';
}
}
if (len <= 0) {
len = 0;
}
break;
}
if (valLen) {
*valLen = len;
}
}
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
/**
* Internal return last HDBC or HSTMT error message.
* @param env environment handle or NULL
* @param dbc database connection handle or NULL
* @param stmt statement handle or NULL
* @param sqlState output buffer for SQL state
* @param nativeErr output buffer for native error code
* @param errmsg output buffer for error message
* @param errmax length of output buffer for error message
* @param errlen output length of error message
* @result ODBC error code
*/
static SQLRETURN
drverror(SQLHENV env, SQLHDBC dbc, SQLHSTMT stmt,
SQLCHAR *sqlState, SQLINTEGER *nativeErr,
SQLCHAR *errmsg, SQLSMALLINT errmax, SQLSMALLINT *errlen)
{
SQLCHAR dummy0[6];
SQLINTEGER dummy1;
SQLSMALLINT dummy2;
if (env == SQL_NULL_HENV &&
dbc == SQL_NULL_HDBC &&
stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
if (sqlState) {
sqlState[0] = '\0';
} else {
sqlState = dummy0;
}
if (!nativeErr) {
nativeErr = &dummy1;
}
*nativeErr = 0;
if (!errlen) {
errlen = &dummy2;
}
*errlen = 0;
if (errmsg) {
if (errmax > 0) {
errmsg[0] = '\0';
}
} else {
errmsg = dummy0;
errmax = 0;
}
if (stmt) {
STMT *s = (STMT *) stmt;
HSTMT_LOCK(stmt);
if (s->logmsg[0] == '\0') {
HSTMT_UNLOCK(stmt);
goto noerr;
}
*nativeErr = s->naterr;
strcpy((char *) sqlState, s->sqlstate);
if (errmax == SQL_NTS) {
strcpy((char *) errmsg, "[SQLite]");
strcat((char *) errmsg, (char *) s->logmsg);
*errlen = strlen((char *) errmsg);
} else {
strncpy((char *) errmsg, "[SQLite]", errmax);
if (errmax - 8 > 0) {
strncpy((char *) errmsg + 8, (char *) s->logmsg, errmax - 8);
}
*errlen = min(strlen((char *) s->logmsg) + 8, errmax);
}
s->logmsg[0] = '\0';
HSTMT_UNLOCK(stmt);
return SQL_SUCCESS;
}
if (dbc) {
DBC *d = (DBC *) dbc;
HDBC_LOCK(dbc);
if (d->magic != DBC_MAGIC || d->logmsg[0] == '\0') {
HDBC_UNLOCK(dbc);
goto noerr;
}
*nativeErr = d->naterr;
strcpy((char *) sqlState, d->sqlstate);
if (errmax == SQL_NTS) {
strcpy((char *) errmsg, "[SQLite]");
strcat((char *) errmsg, (char *) d->logmsg);
*errlen = strlen((char *) errmsg);
} else {
strncpy((char *) errmsg, "[SQLite]", errmax);
if (errmax - 8 > 0) {
strncpy((char *) errmsg + 8, (char *) d->logmsg, errmax - 8);
}
*errlen = min(strlen((char *) d->logmsg) + 8, errmax);
}
d->logmsg[0] = '\0';
HDBC_UNLOCK(dbc);
return SQL_SUCCESS;
}
noerr:
sqlState[0] = '\0';
errmsg[0] = '\0';
*nativeErr = 0;
*errlen = 0;
return SQL_NO_DATA;
}
#ifndef WINTERFACE
/**
* Return last HDBC or HSTMT error message.
* @param env environment handle or NULL
* @param dbc database connection handle or NULL
* @param stmt statement handle or NULL
* @param sqlState output buffer for SQL state
* @param nativeErr output buffer for native error code
* @param errmsg output buffer for error message
* @param errmax length of output buffer for error message
* @param errlen output length of error message
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLError(SQLHENV env, SQLHDBC dbc, SQLHSTMT stmt,
SQLCHAR *sqlState, SQLINTEGER *nativeErr,
SQLCHAR *errmsg, SQLSMALLINT errmax, SQLSMALLINT *errlen)
{
return drverror(env, dbc, stmt, sqlState, nativeErr,
errmsg, errmax, errlen);
}
#endif
#ifdef WINTERFACE
/**
* Return last HDBC or HSTMT error message (UNICODE version).
* @param env environment handle or NULL
* @param dbc database connection handle or NULL
* @param stmt statement handle or NULL
* @param sqlState output buffer for SQL state
* @param nativeErr output buffer for native error code
* @param errmsg output buffer for error message
* @param errmax length of output buffer for error message
* @param errlen output length of error message
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLErrorW(SQLHENV env, SQLHDBC dbc, SQLHSTMT stmt,
SQLWCHAR *sqlState, SQLINTEGER *nativeErr,
SQLWCHAR *errmsg, SQLSMALLINT errmax, SQLSMALLINT *errlen)
{
char state[16];
SQLSMALLINT len = 0;
SQLRETURN ret;
ret = drverror(env, dbc, stmt, (SQLCHAR *) state, nativeErr,
(SQLCHAR *) errmsg, errmax, &len);
if (ret == SQL_SUCCESS) {
if (sqlState) {
uc_from_utf_buf((SQLCHAR *) state, -1, sqlState,
6 * sizeof (SQLWCHAR));
}
if (errmsg) {
if (len > 0) {
SQLWCHAR *e = NULL;
e = uc_from_utf((SQLCHAR *) errmsg, len);
if (e) {
if (errmax > 0) {
uc_strncpy(errmsg, e, errmax);
e[len] = 0;
len = min(errmax, uc_strlen(e));
} else {
len = uc_strlen(e);
}
uc_free(e);
} else {
len = 0;
}
}
if (len <= 0) {
len = 0;
if (errmax > 0) {
errmsg[0] = 0;
}
}
} else {
len = 0;
}
if (errlen) {
*errlen = len;
}
} else if (ret == SQL_NO_DATA) {
if (sqlState) {
sqlState[0] = 0;
}
if (errmsg) {
if (errmax > 0) {
errmsg[0] = 0;
}
}
if (errlen) {
*errlen = 0;
}
}
return ret;
}
#endif
/**
* Return information for more result sets.
* @param stmt statement handle
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLMoreResults(SQLHSTMT stmt)
{
HSTMT_LOCK(stmt);
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
HSTMT_UNLOCK(stmt);
return SQL_NO_DATA;
}
/**
* SQLite callback during drvprepare(), used to collect column information.
* @param arg user data, actually a statement pointer
* @param ncols number of columns
* @param values value string array
* @param cols column name string array
* @result always 1 to abort sqlite_exec()
*/
static int
selcb(void *arg, int ncols, char **values, char **cols)
{
STMT *s = (STMT *) arg;
if (ncols > 0) {
int i, size;
char *p;
COL *dyncols;
DBC *d = (DBC *) s->dbc;
for (i = size = 0; i < ncols; i++) {
size += 3 + 3 * strlen(cols[i]);
}
dyncols = xmalloc(ncols * sizeof (COL) + size);
if (!dyncols) {
freedyncols(s);
s->ncols = 0;
return 1;
}
p = (char *) (dyncols + ncols);
for (i = 0; i < ncols; i++) {
char *q;
dyncols[i].db = ((DBC *) (s->dbc))->dbname;
strcpy(p, cols[i]);
dyncols[i].label = p;
p += strlen(p) + 1;
q = strchr(cols[i], '.');
if (q) {
dyncols[i].table = p;
strncpy(p, cols[i], q - cols[i]);
p[q - cols[i]] = '\0';
p += strlen(p) + 1;
strcpy(p, q + 1);
dyncols[i].column = p;
p += strlen(p) + 1;
} else {
dyncols[i].table = "";
strcpy(p, cols[i]);
dyncols[i].column = p;
p += strlen(p) + 1;
}
if (s->longnames) {
dyncols[i].column = dyncols[i].label;
}
#ifdef SQL_LONGVARCHAR
dyncols[i].type = SQL_LONGVARCHAR;
dyncols[i].size = 65535;
#else
dyncols[i].type = SQL_VARCHAR;
dyncols[i].size = 255;
#endif
dyncols[i].index = i;
dyncols[i].scale = 0;
dyncols[i].prec = 0;
dyncols[i].nosign = 1;
dyncols[i].autoinc = SQL_FALSE;
dyncols[i].notnull = SQL_NULLABLE;
dyncols[i].typename = NULL;
}
freedyncols(s);
s->dyncols = s->cols = dyncols;
s->dcols = ncols;
fixupdyncols(s, d->sqlite, (const char **) cols + ncols);
}
s->ncols = ncols;
return 1;
}
/**
* Internal query preparation used by SQLPrepare() and SQLExecDirect().
* @param stmt statement handle
* @param query query string
* @param queryLen length of query string or SQL_NTS
* @result ODBC error code
*/
static SQLRETURN
drvprepare(SQLHSTMT stmt, SQLCHAR *query, SQLINTEGER queryLen)
{
STMT *s;
DBC *d;
char *errp = NULL;
SQLRETURN sret;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (s->dbc == SQL_NULL_HDBC) {
noconn:
return noconn(s);
}
d = s->dbc;
if (!d->sqlite) {
goto noconn;
}
vm_end(s);
sret = starttran(s);
if (sret != SQL_SUCCESS) {
return sret;
}
freep(&s->query);
s->query = (SQLCHAR *) fixupsql((char *) query, queryLen, &s->nparams,
&s->isselect, &errp,
d->version, &s->parmnames);
if (!s->query) {
if (errp) {
setstat(s, -1, errp, (*s->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
return nomem(s);
}
#ifdef CANT_PASS_VALIST_AS_CHARPTR
if (s->nparams > MAX_PARAMS_FOR_VPRINTF) {
freep(&s->query);
setstat(s, -1, "too much parameters in query",
(*s->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
#endif
errp = NULL;
freeresult(s, -1);
if (s->isselect > 0) {
int ret;
char **params = NULL;
if (s->nparams) {
int i, maxp;
#ifdef CANT_PASS_VALIST_AS_CHARPTR
maxp = MAX_PARAMS_FOR_VPRINTF;
#else
maxp = s->nparams;
#endif
params = xmalloc(maxp * sizeof (char *));
if (!params) {
return nomem(s);
}
for (i = 0; i < maxp; i++) {
params[i] = NULL;
}
}
#ifdef CANT_PASS_VALIST_AS_CHARPTR
if (params) {
ret = sqlite_exec_printf(d->sqlite, (char *) s->query, selcb, s,
&errp,
params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7],
params[8], params[9],
params[10], params[11],
params[12], params[13],
params[14], params[15],
params[16], params[17],
params[18], params[19],
params[20], params[21],
params[22], params[23],
params[24], params[25],
params[26], params[27],
params[28], params[29],
params[30], params[31]);
} else {
ret = sqlite_exec_printf(d->sqlite, (char *) s->query, selcb,
s, &errp);
}
#else
ret = sqlite_exec_vprintf(d->sqlite, (char *) s->query, selcb, s,
&errp, (char *) params);
#endif
if (ret != SQLITE_ABORT && ret != SQLITE_OK) {
dbtracerc(d, ret, errp);
freep(&params);
setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", ret);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
return SQL_ERROR;
}
freep(&params);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
}
mkbindcols(s, s->ncols);
if (s->nparams) {
s->paramset_count = 0;
}
return SQL_SUCCESS;
}
/**
* Internal query execution used by SQLExecute() and SQLExecDirect().
* @param stmt statement handle
* @param initial false when called from SQLPutData()
* @result ODBC error code
*/
static SQLRETURN
drvexecute(SQLHSTMT stmt, int initial)
{
STMT *s;
DBC *d;
char *errp = NULL, **params = NULL;
int rc, i, ncols;
SQLRETURN ret;
if (stmt == SQL_NULL_HSTMT) {
return SQL_INVALID_HANDLE;
}
s = (STMT *) stmt;
if (s->dbc == SQL_NULL_HDBC) {
noconn:
return noconn(s);
}
d = (DBC *) s->dbc;
if (!d->sqlite) {
goto noconn;
}
if (!s->query) {
setstat(s, -1, "no query prepared", (*s->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
if (s->nbindparms < s->nparams) {
unbound:
setstat(s, -1, "unbound parameters in query",
(*s->ov3) ? "HY000" : "S1000");
return SQL_ERROR;
}
for (i = 0; i < s->nparams; i++) {
BINDPARM *p = &s->bindparms[i];
if (!p->bound) {
goto unbound;
}
}
ret = starttran(s);
if (ret != SQL_SUCCESS) {
goto cleanup;
}
again:
vm_end(s);
if (initial) {
/* fixup data-at-execution parameters and alloc'ed blobs */
for (i = 0; i < s->nparams; i++) {
BINDPARM *p = &s->bindparms[i];
if (p->param == p->parbuf) {
p->param = NULL;
}
freep(&p->parbuf);
if (p->need <= 0 &&
p->lenp && *p->lenp <= SQL_LEN_DATA_AT_EXEC_OFFSET) {
p->need = 1;
p->offs = 0;
p->len = 0;
}
}
}
if (s->nparams) {
int maxp;
#ifdef CANT_PASS_VALIST_AS_CHARPTR
maxp = MAX_PARAMS_FOR_VPRINTF;
#else
maxp = s->nparams;
#endif
params = xmalloc(maxp * sizeof (char *));
if (!params) {
ret = nomem(s);
goto cleanup;
}
for (i = 0; i < maxp; i++) {
params[i] = NULL;
}
for (i = 0; i < s->nparams; i++) {
ret = substparam(s, i, &params[i]);
if (ret != SQL_SUCCESS) {
freep(&params);
goto cleanup;
}
}
}
freeresult(s, 0);
if (s->isselect > 0 && !d->intrans &&
s->curtype == SQL_CURSOR_FORWARD_ONLY &&
d->step_enable && s->nparams == 0 && d->vm_stmt == NULL) {
s->nrows = -1;
ret = vm_start(s, params);
if (ret == SQL_SUCCESS) {
freep(&params);
goto done2;
}
}
#ifdef CANT_PASS_VALIST_AS_CHARPTR
if (params) {
rc = sqlite_get_table_printf(d->sqlite, (char *) s->query, &s->rows,
&s->nrows, &ncols, &errp,
params[0], params[1],
params[2], params[3],
params[4], params[5],
params[6], params[7],
params[8], params[9],
params[10], params[11],
params[12], params[13],
params[14], params[15],
params[16], params[17],
params[18], params[19],
params[20], params[21],
params[22], params[23],
params[24], params[25],
params[26], params[27],
params[28], params[29],
params[30], params[31]);
} else {
rc = sqlite_get_table_printf(d->sqlite, (char *) s->query, &s->rows,
&s->nrows, &ncols, &errp);
}
#else
rc = sqlite_get_table_vprintf(d->sqlite, (char *) s->query, &s->rows,
&s->nrows, &ncols, &errp, (char *) params);
#endif
dbtracerc(d, rc, errp);
if (rc != SQLITE_OK) {
freep(&params);
setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000",
errp ? errp : "unknown error", rc);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
ret = SQL_ERROR;
goto cleanup;
}
freep(&params);
if (errp) {
sqlite_freemem(errp);
errp = NULL;
}
s->rowfree = sqlite_free_table;
if (ncols == 1 && !s->isselect) {
/*
* INSERT/UPDATE/DELETE results are immediately released,
* but the row count is retained for SQLRowCount().
*/
if (strcmp(s->rows[0], "rows inserted") == 0 ||
strcmp(s->rows[0], "rows updated") == 0 ||
strcmp(s->rows[0], "rows deleted") == 0) {
int nrows = 0;
nrows = strtol(s->rows[1], NULL, 0);
freeresult(s, -1);
s->nrows = nrows;
goto done;
}
}
if (s->ncols != ncols) {
int size;
char *p;
COL *dyncols;
for (i = size = 0; i < ncols; i++) {
size += 3 + 3 * strlen(s->rows[i]);
}
if (size == 0) {
freeresult(s, -1);
goto done;
}
dyncols = xmalloc(ncols * sizeof (COL) + size);
if (!dyncols) {
ret = nomem(s);
goto cleanup;
}
p = (char *) (dyncols + ncols);
for (i = 0; i < ncols; i++) {
char *q;
dyncols[i].db = d->dbname;
strcpy(p, s->rows[i]);
dyncols[i].label = p;
p += strlen(p) + 1;
q = strchr(s->rows[i], '.');
if (q) {
dyncols[i].table = p;
strncpy(p, s->rows[i], q - s->rows[i]);
p[q - s->rows[i]] = '\0';
p += strlen(p) + 1;
dyncols[i].column = q + 1;
} else {
dyncols[i].table = "";
dyncols[i].column = s->rows[i];
}
if (s->longnames) {
dyncols[i].column = dyncols[i].label;
}
#ifdef SQL_LONGVARCHAR
dyncols[i].type = SQL_LONGVARCHAR;
dyncols[i].size = 65536;
#else
dyncols[i].type = SQL_VARCHAR;
dyncols[i].size = 255;
#endif
dyncols[i].index = i;
dyncols[i].scale = 0;
dyncols[i].prec = 0;
dyncols[i].nosign = 1;
dyncols[i].autoinc = SQL_FALSE;
dyncols[i].notnull = SQL_NULLABLE;
dyncols[i].typename = NULL;
}
freedyncols(s);
s->ncols = s->dcols = ncols;
s->dyncols = s->cols = dyncols;
fixupdyncols(s, d->sqlite, NULL);
}
done:
mkbindcols(s, s->ncols);
done2:
ret = SQL_SUCCESS;
s->rowp = -1;
if (s->nparams) {
s->paramset_count++;
s->paramset_nrows += s->nrows;
if (s->paramset_count < s->paramset_size) {
for (i = 0; i < s->nparams; i++) {
BINDPARM *p = &s->bindparms[i];
if (p->param == p->parbuf) {
p->param = NULL;
}
freep(&p->parbuf);
if (p->lenp0 &&
s->parm_bind_type != SQL_PARAM_BIND_BY_COLUMN) {
p->lenp = (SQLLEN *) ((char *) p->lenp0 +
s->paramset_count * s->parm_bind_type);
} else if (p->lenp0 && p->inc > 0) {
p->lenp = p->lenp0 + s->paramset_count;
}
if (!p->lenp || *p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET) {
if (p->param0 &&
s->parm_bind_type != SQL_PARAM_BIND_BY_COLUMN) {
p->param = (char *) p->param0 +
s->paramset_count * s->parm_bind_type;
} else if (p->param0 && p->inc > 0) {
p->param = (char *) p->param0 +
s->paramset_count * p->inc;
}
}
}
goto again;
}
}
cleanup:
if (ret != SQL_NEED_DATA && s->nparams) {
for (i = 0; i < s->nparams; i++) {
BINDPARM *p = &s->bindparms[i];
if (p->param == p->parbuf) {
p->param = NULL;
}
freep(&p->parbuf);
if (!p->lenp || *p->lenp > SQL_LEN_DATA_AT_EXEC_OFFSET) {
p->param = p->param0;
}
p->lenp = p->lenp0;
}
s->nrows = s->paramset_nrows;
s->paramset_count = 0;
s->paramset_nrows = 0;
}
return ret;
}
#ifndef WINTERFACE
/**
* Prepare HSTMT.
* @param stmt statement handle
* @param query query string
* @param queryLen length of query string or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLPrepare(SQLHSTMT stmt, SQLCHAR *query, SQLINTEGER queryLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvprepare(stmt, query, queryLen);
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Prepare HSTMT (UNICODE version).
* @param stmt statement handle
* @param query query string
* @param queryLen length of query string or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLPrepareW(SQLHSTMT stmt, SQLWCHAR *query, SQLINTEGER queryLen)
{
SQLRETURN ret;
char *q = uc_to_utf_c(query, queryLen);
HSTMT_LOCK(stmt);
if (!q) {
ret = nomem((STMT *) stmt);
goto done;
}
ret = drvprepare(stmt, (SQLCHAR *) q, SQL_NTS);
uc_free(q);
done:
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
/**
* Execute query.
* @param stmt statement handle
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLExecute(SQLHSTMT stmt)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvexecute(stmt, 1);
HSTMT_UNLOCK(stmt);
return ret;
}
#ifndef WINTERFACE
/**
* Execute query directly.
* @param stmt statement handle
* @param query query string
* @param queryLen length of query string or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLExecDirect(SQLHSTMT stmt, SQLCHAR *query, SQLINTEGER queryLen)
{
SQLRETURN ret;
HSTMT_LOCK(stmt);
ret = drvprepare(stmt, query, queryLen);
if (ret == SQL_SUCCESS) {
ret = drvexecute(stmt, 1);
}
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Execute query directly (UNICODE version).
* @param stmt statement handle
* @param query query string
* @param queryLen length of query string or SQL_NTS
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLExecDirectW(SQLHSTMT stmt, SQLWCHAR *query, SQLINTEGER queryLen)
{
SQLRETURN ret;
char *q = uc_to_utf_c(query, queryLen);
HSTMT_LOCK(stmt);
if (!q) {
ret = nomem((STMT *) stmt);
goto done;
}
ret = drvprepare(stmt, (SQLCHAR *) q, SQL_NTS);
uc_free(q);
if (ret == SQL_SUCCESS) {
ret = drvexecute(stmt, 1);
}
done:
HSTMT_UNLOCK(stmt);
return ret;
}
#endif
#if defined(_WIN32) || defined(_WIN64) || defined(__OS2__)
#ifndef WITHOUT_DRIVERMGR
#if defined(_WIN32) || defined(_WIN64)
/*
* Windows configuration dialog stuff.
*/
#include <windowsx.h>
#include <winuser.h>
#endif
#ifdef __OS2__
#define INCL_WIN
#define INCL_PM
#define INCL_DOSMODULEMGR
#define INCL_DOSERRORS
#define INCL_WINSTDFILE
#include <os2.h>
#include <stdlib.h>
#include "resourceos2.h"
#endif
#define MAXPATHLEN (255+1) /* Max path length */
#define MAXKEYLEN (15+1) /* Max keyword length */
#define MAXDESC (255+1) /* Max description length */
#define MAXDSNAME (32+1) /* Max data source name length */
#define MAXTONAME (32+1) /* Max timeout length */
#define MAXDBNAME (255+1)
/* Attribute key indexes into an array of Attr structs, see below */
#define KEY_DSN 0
#define KEY_DESC 1
#define KEY_DBNAME 2
#define KEY_BUSY 3
#define KEY_DRIVER 4
#define KEY_NOWCHAR 5
#define KEY_STEPAPI 6
#define KEY_NOTXN 7
#define KEY_LONGNAM 8
#define NUMOFKEYS 9
typedef struct {
BOOL supplied;
char attr[MAXPATHLEN];
} ATTR;
typedef struct {
SQLHWND parent;
#ifdef __OS2__
HMODULE hDLL;
#endif
LPCSTR driver;
ATTR attr[NUMOFKEYS];
char DSN[MAXDSNAME];
BOOL newDSN;
BOOL defDSN;
} SETUPDLG;
static struct {
char *key;
int ikey;
} attrLookup[] = {
{ "DSN", KEY_DSN },
{ "DESC", KEY_DESC },
{ "Description", KEY_DESC},
{ "Database", KEY_DBNAME },
{ "Timeout", KEY_BUSY },
{ "Driver", KEY_DRIVER },
{ "NoWCHAR", KEY_NOWCHAR },
{ "StepAPI", KEY_STEPAPI },
{ "NoTXN", KEY_NOTXN },
{ "LongNames", KEY_LONGNAM },
{ NULL, 0 }
};
/**
* Setup dialog data from datasource attributes.
* @param attribs attribute string
* @param setupdlg pointer to dialog data
*/
static void
ParseAttributes(LPCSTR attribs, SETUPDLG *setupdlg)
{
char *str = (char *) attribs, *start, key[MAXKEYLEN];
int elem, nkey;
while (*str) {
start = str;
if ((str = strchr(str, '=')) == NULL) {
return;
}
elem = -1;
nkey = str - start;
if (nkey < sizeof (key)) {
int i;
memcpy(key, start, nkey);
key[nkey] = '\0';
for (i = 0; attrLookup[i].key; i++) {
if (strcasecmp(attrLookup[i].key, key) == 0) {
elem = attrLookup[i].ikey;
break;
}
}
}
start = ++str;
while (*str && *str != ';') {
++str;
}
if (elem >= 0) {
int end = min(str - start, sizeof (setupdlg->attr[elem].attr) - 1);
setupdlg->attr[elem].supplied = TRUE;
memcpy(setupdlg->attr[elem].attr, start, end);
setupdlg->attr[elem].attr[end] = '\0';
}
++str;
}
}
/**
* Set datasource attributes in registry.
* @param parent handle of parent window
* @param setupdlg pointer to dialog data
* @result true or false
*/
static BOOL
SetDSNAttributes(HWND parent, SETUPDLG *setupdlg)
{
char *dsn = setupdlg->attr[KEY_DSN].attr;
if (setupdlg->newDSN && strlen(dsn) == 0) {
return FALSE;
}
if (!SQLWriteDSNToIni(dsn, setupdlg->driver)) {
if (parent) {
#if defined(_WIN32) || defined(_WIN64)
char buf[MAXPATHLEN], msg[MAXPATHLEN];
LoadString(hModule, IDS_BADDSN, buf, sizeof (buf));
wsprintf(msg, buf, dsn);
LoadString(hModule, IDS_MSGTITLE, buf, sizeof (buf));
MessageBox(parent, msg, buf,
MB_ICONEXCLAMATION | MB_OK | MB_TASKMODAL |
MB_SETFOREGROUND);
#endif
#ifdef __OS2__
WinMessageBox(HWND_DESKTOP, parent, "Bad DSN", "Configure Error",
0, MB_ERROR | MB_OK );
#endif
}
return FALSE;
}
if (parent || setupdlg->attr[KEY_DESC].supplied) {
SQLWritePrivateProfileString(dsn, "Description",
setupdlg->attr[KEY_DESC].attr,
ODBC_INI);
}
if (parent || setupdlg->attr[KEY_DBNAME].supplied) {
SQLWritePrivateProfileString(dsn, "Database",
setupdlg->attr[KEY_DBNAME].attr,
ODBC_INI);
}
if (parent || setupdlg->attr[KEY_BUSY].supplied) {
SQLWritePrivateProfileString(dsn, "Timeout",
setupdlg->attr[KEY_BUSY].attr,
ODBC_INI);
}
if (parent || setupdlg->attr[KEY_NOWCHAR].supplied) {
SQLWritePrivateProfileString(dsn, "NoWCHAR",
setupdlg->attr[KEY_NOWCHAR].attr,
ODBC_INI);
}
if (parent || setupdlg->attr[KEY_STEPAPI].supplied) {
SQLWritePrivateProfileString(dsn, "StepAPI",
setupdlg->attr[KEY_STEPAPI].attr,
ODBC_INI);
}
if (parent || setupdlg->attr[KEY_NOTXN].supplied) {
SQLWritePrivateProfileString(dsn, "NoTXN",
setupdlg->attr[KEY_NOTXN].attr,
ODBC_INI);
}
if (parent || setupdlg->attr[KEY_LONGNAM].supplied) {
SQLWritePrivateProfileString(dsn, "LongNames",
setupdlg->attr[KEY_LONGNAM].attr,
ODBC_INI);
}
if (setupdlg->attr[KEY_DSN].supplied &&
strcasecmp(setupdlg->DSN, setupdlg->attr[KEY_DSN].attr)) {
SQLRemoveDSNFromIni(setupdlg->DSN);
}
return TRUE;
}
/**
* Get datasource attributes from registry.
* @param setupdlg pointer to dialog data
*/
static void
GetAttributes(SETUPDLG *setupdlg)
{
char *dsn = setupdlg->attr[KEY_DSN].attr;
if (!setupdlg->attr[KEY_DESC].supplied) {
SQLGetPrivateProfileString(dsn, "Description", "",
setupdlg->attr[KEY_DESC].attr,
sizeof (setupdlg->attr[KEY_DESC].attr),
ODBC_INI);
}
if (!setupdlg->attr[KEY_DBNAME].supplied) {
SQLGetPrivateProfileString(dsn, "Database", "",
setupdlg->attr[KEY_DBNAME].attr,
sizeof (setupdlg->attr[KEY_DBNAME].attr),
ODBC_INI);
}
if (!setupdlg->attr[KEY_BUSY].supplied) {
SQLGetPrivateProfileString(dsn, "Timeout", "100000",
setupdlg->attr[KEY_BUSY].attr,
sizeof (setupdlg->attr[KEY_BUSY].attr),
ODBC_INI);
}
if (!setupdlg->attr[KEY_NOWCHAR].supplied) {
SQLGetPrivateProfileString(dsn, "NoWCHAR", "",
setupdlg->attr[KEY_NOWCHAR].attr,
sizeof (setupdlg->attr[KEY_NOWCHAR].attr),
ODBC_INI);
}
if (!setupdlg->attr[KEY_STEPAPI].supplied) {
SQLGetPrivateProfileString(dsn, "StepAPI", "0",
setupdlg->attr[KEY_STEPAPI].attr,
sizeof (setupdlg->attr[KEY_STEPAPI].attr),
ODBC_INI);
}
if (!setupdlg->attr[KEY_NOTXN].supplied) {
SQLGetPrivateProfileString(dsn, "NoTXN", "",
setupdlg->attr[KEY_NOTXN].attr,
sizeof (setupdlg->attr[KEY_NOTXN].attr),
ODBC_INI);
}
if (!setupdlg->attr[KEY_LONGNAM].supplied) {
SQLGetPrivateProfileString(dsn, "LongNames", "",
setupdlg->attr[KEY_LONGNAM].attr,
sizeof (setupdlg->attr[KEY_LONGNAM].attr),
ODBC_INI);
}
}
/**
* Open file dialog for selection of SQLite database file.
* @param hdlg handle of originating dialog window
*/
static void
GetDBFile(HWND hdlg)
{
#if defined(_WIN32) || defined(_WIN64)
#ifdef _WIN64
SETUPDLG *setupdlg = (SETUPDLG *) GetWindowLongPtr(hdlg, DWLP_USER);
#else
SETUPDLG *setupdlg = (SETUPDLG *) GetWindowLong(hdlg, DWL_USER);
#endif
OPENFILENAME ofn;
memset(&ofn, 0, sizeof (ofn));
ofn.lStructSize = sizeof (ofn);
ofn.hwndOwner = hdlg;
#ifdef _WIN64
ofn.hInstance = (HINSTANCE) GetWindowLongPtr(hdlg, GWLP_HINSTANCE);
#else
ofn.hInstance = (HINSTANCE) GetWindowLong(hdlg, GWL_HINSTANCE);
#endif
ofn.lpstrFile = (LPTSTR) setupdlg->attr[KEY_DBNAME].attr;
ofn.nMaxFile = MAXPATHLEN;
ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST |
OFN_NOCHANGEDIR | OFN_EXPLORER | OFN_FILEMUSTEXIST;
if (GetOpenFileName(&ofn)) {
SetDlgItemText(hdlg, IDC_DBNAME, setupdlg->attr[KEY_DBNAME].attr);
setupdlg->attr[KEY_DBNAME].supplied = TRUE;
}
#endif
#ifdef __OS2__
SETUPDLG *setupdlg = (SETUPDLG *) WinQueryWindowULong(hdlg, QWL_USER);
FILEDLG ofn;
memset(&ofn, 0, sizeof (ofn));
ofn.cbSize = sizeof (FILEDLG);
strcpy(ofn.szFullFile, setupdlg->attr[KEY_DBNAME].attr);
ofn.fl = FDS_OPEN_DIALOG | FDS_CENTER;
WinFileDlg(hdlg, hdlg, &ofn);
if (ofn.lReturn == DID_OK) {
strcpy(setupdlg->attr[KEY_DBNAME].attr, ofn.szFullFile);
WinSetDlgItemText(hdlg, EF_DATABASE, setupdlg->attr[KEY_DBNAME].attr);
setupdlg->attr[KEY_DBNAME].supplied = TRUE;
}
#endif
}
#if defined(_WIN32) || defined(_WIN64)
/**
* Dialog procedure for ConfigDSN().
* @param hdlg handle of dialog window
* @param wmsg type of message
* @param wparam wparam of message
* @param lparam lparam of message
* @result true or false
*/
static BOOL CALLBACK
ConfigDlgProc(HWND hdlg, WORD wmsg, WPARAM wparam, LPARAM lparam)
{
SETUPDLG *setupdlg = NULL;
switch (wmsg) {
case WM_INITDIALOG:
#ifdef _WIN64
SetWindowLong(hdlg, DWLP_USER, lparam);
#else
SetWindowLong(hdlg, DWL_USER, lparam);
#endif
setupdlg = (SETUPDLG *) lparam;
GetAttributes(setupdlg);
SetDlgItemText(hdlg, IDC_DSNAME, setupdlg->attr[KEY_DSN].attr);
SetDlgItemText(hdlg, IDC_DESC, setupdlg->attr[KEY_DESC].attr);
SetDlgItemText(hdlg, IDC_DBNAME, setupdlg->attr[KEY_DBNAME].attr);
SetDlgItemText(hdlg, IDC_TONAME, setupdlg->attr[KEY_BUSY].attr);
SendDlgItemMessage(hdlg, IDC_DSNAME, EM_LIMITTEXT,
(WPARAM) (MAXDSNAME - 1), (LPARAM) 0);
SendDlgItemMessage(hdlg, IDC_DESC, EM_LIMITTEXT,
(WPARAM) (MAXDESC - 1), (LPARAM) 0);
SendDlgItemMessage(hdlg, IDC_DBNAME, EM_LIMITTEXT,
(WPARAM) (MAXDBNAME - 1), (LPARAM) 0);
SendDlgItemMessage(hdlg, IDC_TONAME, EM_LIMITTEXT,
(WPARAM) (MAXTONAME - 1), (LPARAM) 0);
CheckDlgButton(hdlg, IDC_NOWCHAR,
getbool(setupdlg->attr[KEY_NOWCHAR].attr) ?
BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(hdlg, IDC_STEPAPI,
getbool(setupdlg->attr[KEY_STEPAPI].attr) ?
BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(hdlg, IDC_NOTXN,
getbool(setupdlg->attr[KEY_NOTXN].attr) ?
BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(hdlg, IDC_LONGNAM,
getbool(setupdlg->attr[KEY_LONGNAM].attr) ?
BST_CHECKED : BST_UNCHECKED);
if (setupdlg->defDSN) {
EnableWindow(GetDlgItem(hdlg, IDC_DSNAME), FALSE);
EnableWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), FALSE);
}
return TRUE;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wparam, lparam)) {
case IDC_DSNAME:
if (GET_WM_COMMAND_CMD(wparam, lparam) == EN_CHANGE) {
char item[MAXDSNAME];
EnableWindow(GetDlgItem(hdlg, IDOK),
GetDlgItemText(hdlg, IDC_DSNAME,
item, sizeof (item)));
return TRUE;
}
break;
case IDC_BROWSE:
GetDBFile(hdlg);
break;
case IDOK:
#ifdef _WIN64
setupdlg = (SETUPDLG *) GetWindowLongPtr(hdlg, DWLP_USER);
#else
setupdlg = (SETUPDLG *) GetWindowLong(hdlg, DWL_USER);
#endif
if (!setupdlg->defDSN) {
GetDlgItemText(hdlg, IDC_DSNAME,
setupdlg->attr[KEY_DSN].attr,
sizeof (setupdlg->attr[KEY_DSN].attr));
}
GetDlgItemText(hdlg, IDC_DESC,
setupdlg->attr[KEY_DESC].attr,
sizeof (setupdlg->attr[KEY_DESC].attr));
GetDlgItemText(hdlg, IDC_DBNAME,
setupdlg->attr[KEY_DBNAME].attr,
sizeof (setupdlg->attr[KEY_DBNAME].attr));
GetDlgItemText(hdlg, IDC_TONAME,
setupdlg->attr[KEY_BUSY].attr,
sizeof (setupdlg->attr[KEY_BUSY].attr));
strcpy(setupdlg->attr[KEY_NOWCHAR].attr,
(IsDlgButtonChecked(hdlg, IDC_NOWCHAR) == BST_CHECKED) ?
"1" : "0");
strcpy(setupdlg->attr[KEY_STEPAPI].attr,
(IsDlgButtonChecked(hdlg, IDC_STEPAPI) == BST_CHECKED) ?
"1" : "0");
strcpy(setupdlg->attr[KEY_NOTXN].attr,
(IsDlgButtonChecked(hdlg, IDC_NOTXN) == BST_CHECKED) ?
"1" : "0");
strcpy(setupdlg->attr[KEY_LONGNAM].attr,
(IsDlgButtonChecked(hdlg, IDC_LONGNAM) == BST_CHECKED) ?
"1" : "0");
SetDSNAttributes(hdlg, setupdlg);
/* FALL THROUGH */
case IDCANCEL:
EndDialog(hdlg, wparam);
return TRUE;
}
break;
}
return FALSE;
}
#endif
#ifdef __OS2__
/**
* Dialog procedure for ConfigDSN().
* @param hdlg handle of dialog window
* @param wmsg type of message
* @param wparam wparam of message
* @param lparam lparam of message
* @result standard MRESULT
*/
static MRESULT EXPENTRY
ConfigDlgProc(HWND hdlg, ULONG wmsg, MPARAM wparam, MPARAM lparam)
{
SETUPDLG *setupdlg = NULL;
switch (wmsg) {
case WM_INITDLG:
WinSetWindowULong(hdlg, QWL_USER, (ULONG) lparam);
setupdlg = (SETUPDLG *) lparam;
GetAttributes(setupdlg);
WinSetDlgItemText(hdlg, EF_DSNNAME, setupdlg->attr[KEY_DSN].attr);
WinSetDlgItemText(hdlg, EF_DSNDESC, setupdlg->attr[KEY_DESC].attr);
WinSetDlgItemText(hdlg, EF_DATABASE, setupdlg->attr[KEY_DBNAME].attr);
WinSetDlgItemText(hdlg, EF_TIMEOUT, setupdlg->attr[KEY_BUSY].attr);
WinSendDlgItemMsg(hdlg, EF_DSNNAME, EM_SETTEXTLIMIT,
MPFROMSHORT(MAXDSNAME - 1), 0L);
WinSendDlgItemMsg(hdlg, EF_DSNDESC, EM_SETTEXTLIMIT,
MPFROMSHORT(MAXDESC - 1), 0L);
WinSendDlgItemMsg(hdlg, EF_DATABASE, EM_SETTEXTLIMIT,
MPFROMSHORT(MAXDBNAME - 1), 0L);
WinSendDlgItemMsg(hdlg, EF_TIMEOUT, EM_SETTEXTLIMIT,
MPFROMSHORT(MAXTONAME - 1), 0L);
WinCheckButton(hdlg, IDC_NOWCHAR,
getbool(setupdlg->attr[KEY_NOWCHAR].attr));
WinCheckButton(hdlg, IDC_STEPAPI,
getbool(setupdlg->attr[KEY_STEPAPI].attr));
WinCheckButton(hdlg, IDC_NOTXN,
getbool(setupdlg->attr[KEY_NOTXN].attr));
WinCheckButton(hdlg, IDC_LONGNAM,
getbool(setupdlg->attr[KEY_LONGNAM].attr));
if (setupdlg->defDSN) {
WinEnableWindow(WinWindowFromID(hdlg, EF_DSNNAME), FALSE);
}
return 0;
case WM_COMMAND:
switch (SHORT1FROMMP(wparam)) {
case IDC_BROWSE:
GetDBFile(hdlg);
break;
case DID_OK:
setupdlg = (SETUPDLG *) WinQueryWindowULong(hdlg, QWL_USER);
if (!setupdlg->defDSN) {
WinQueryDlgItemText(hdlg, EF_DSNNAME,
sizeof (setupdlg->attr[KEY_DSN].attr),
setupdlg->attr[KEY_DSN].attr);
}
WinQueryDlgItemText(hdlg, EF_DSNDESC,
sizeof (setupdlg->attr[KEY_DESC].attr),
setupdlg->attr[KEY_DESC].attr);
WinQueryDlgItemText(hdlg, EF_DATABASE,
sizeof (setupdlg->attr[KEY_DBNAME].attr),
setupdlg->attr[KEY_DBNAME].attr);
WinQueryDlgItemText(hdlg, EF_TIMEOUT,
sizeof (setupdlg->attr[KEY_BUSY].attr),
setupdlg->attr[KEY_BUSY].attr);
if ((ULONG)WinSendDlgItemMsg(hdlg, IDC_NOWCHAR, BM_QUERYCHECK,
0, 0) == 1) {
strcpy(setupdlg->attr[KEY_NOWCHAR].attr, "1");
} else {
strcpy(setupdlg->attr[KEY_NOWCHAR].attr, "0");
}
if ((ULONG)WinSendDlgItemMsg(hdlg, IDC_STEPAPI, BM_QUERYCHECK,
0, 0) == 1) {
strcpy(setupdlg->attr[KEY_STEPAPI].attr, "1");
} else {
strcpy(setupdlg->attr[KEY_STEPAPI].attr, "0");
}
if ((ULONG)WinSendDlgItemMsg(hdlg, IDC_NOTXN, BM_QUERYCHECK,
0, 0) == 1) {
strcpy(setupdlg->attr[KEY_NOTXN].attr, "1");
} else {
strcpy(setupdlg->attr[KEY_NOTXN].attr, "0");
}
if ((ULONG)WinSendDlgItemMsg(hdlg, IDC_LONGNAM, BM_QUERYCHECK,
0, 0) == 1) {
strcpy(setupdlg->attr[KEY_LONGNAM].attr, "1");
} else {
strcpy(setupdlg->attr[KEY_LONGNAM].attr, "0");
}
SetDSNAttributes(hdlg, setupdlg);
WinDismissDlg(hdlg, DID_OK);
return 0;
case DID_CANCEL:
WinDismissDlg(hdlg, DID_OK);
return 0;
}
break;
}
return WinDefDlgProc(hdlg, wmsg, wparam, lparam);
}
#endif
/**
* ODBC INSTAPI procedure for DSN configuration.
* @param hwnd parent window handle
* @param request type of request
* @param driver driver name
* @param attribs attribute string of DSN
* @result true or false
*/
BOOL INSTAPI
ConfigDSN(HWND hwnd, WORD request, LPCSTR driver, LPCSTR attribs)
{
BOOL success;
SETUPDLG *setupdlg;
setupdlg = (SETUPDLG *) xmalloc(sizeof (SETUPDLG));
if (setupdlg == NULL) {
return FALSE;
}
memset(setupdlg, 0, sizeof (SETUPDLG));
if (attribs) {
ParseAttributes(attribs, setupdlg);
}
if (setupdlg->attr[KEY_DSN].supplied) {
strcpy(setupdlg->DSN, setupdlg->attr[KEY_DSN].attr);
} else {
setupdlg->DSN[0] = '\0';
}
if (request == ODBC_REMOVE_DSN) {
if (!setupdlg->attr[KEY_DSN].supplied) {
success = FALSE;
} else {
success = SQLRemoveDSNFromIni(setupdlg->attr[KEY_DSN].attr);
}
} else {
#if defined(_WIN32) || defined(_WIN64)
setupdlg->parent = hwnd;
#endif
#ifdef __OS2__
setupdlg->parent = (SQLHWND) hwnd;
#endif
setupdlg->driver = driver;
setupdlg->newDSN = request == ODBC_ADD_DSN;
setupdlg->defDSN = strcasecmp(setupdlg->attr[KEY_DSN].attr,
"Default") == 0;
if (hwnd) {
#if defined(_WIN32) || defined(_WIN64)
success = DialogBoxParam(hModule, MAKEINTRESOURCE(CONFIGDSN),
hwnd, (DLGPROC) ConfigDlgProc,
(LPARAM) setupdlg) == IDOK;
#endif
#ifdef __OS2__
HMODULE hDLL;
DosQueryModuleHandle("SQLLODBC.DLL", &hDLL);
setupdlg->hDLL = hDLL;
success = WinDlgBox(HWND_DESKTOP, hwnd, ConfigDlgProc, hDLL,
DLG_SQLITEODBCCONFIGURATION, setupdlg);
success = (success == DID_OK) ? TRUE : FALSE;
#endif
} else if (setupdlg->attr[KEY_DSN].supplied) {
success = SetDSNAttributes(hwnd, setupdlg);
} else {
success = FALSE;
}
}
xfree(setupdlg);
return success;
}
#if defined(_WIN32) || defined(_WIN64)
/**
* Dialog procedure for SQLDriverConnect().
* @param hdlg handle of dialog window
* @param wmsg type of message
* @param wparam wparam of message
* @param lparam lparam of message
* @result true or false
*/
static BOOL CALLBACK
DriverConnectProc(HWND hdlg, WORD wmsg, WPARAM wparam, LPARAM lparam)
{
SETUPDLG *setupdlg;
switch (wmsg) {
case WM_INITDIALOG:
#ifdef _WIN64
SetWindowLong(hdlg, DWLP_USER, lparam);
#else
SetWindowLong(hdlg, DWL_USER, lparam);
#endif
setupdlg = (SETUPDLG *) lparam;
SetDlgItemText(hdlg, IDC_DSNAME, setupdlg->attr[KEY_DSN].attr);
SetDlgItemText(hdlg, IDC_DESC, setupdlg->attr[KEY_DESC].attr);
SetDlgItemText(hdlg, IDC_DBNAME, setupdlg->attr[KEY_DBNAME].attr);
SetDlgItemText(hdlg, IDC_TONAME, setupdlg->attr[KEY_BUSY].attr);
SendDlgItemMessage(hdlg, IDC_DSNAME, EM_LIMITTEXT,
(WPARAM) (MAXDSNAME - 1), (LPARAM) 0);
SendDlgItemMessage(hdlg, IDC_DESC, EM_LIMITTEXT,
(WPARAM) (MAXDESC - 1), (LPARAM) 0);
SendDlgItemMessage(hdlg, IDC_DBNAME, EM_LIMITTEXT,
(WPARAM) (MAXDBNAME - 1), (LPARAM) 0);
SendDlgItemMessage(hdlg, IDC_TONAME, EM_LIMITTEXT,
(WPARAM) (MAXTONAME - 1), (LPARAM) 0);
CheckDlgButton(hdlg, IDC_NOWCHAR,
getbool(setupdlg->attr[KEY_NOWCHAR].attr) ?
BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(hdlg, IDC_STEPAPI,
getbool(setupdlg->attr[KEY_STEPAPI].attr) ?
BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(hdlg, IDC_NOTXN,
getbool(setupdlg->attr[KEY_NOTXN].attr) ?
BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(hdlg, IDC_LONGNAM,
getbool(setupdlg->attr[KEY_LONGNAM].attr) ?
BST_CHECKED : BST_UNCHECKED);
if (setupdlg->defDSN) {
EnableWindow(GetDlgItem(hdlg, IDC_DSNAME), FALSE);
EnableWindow(GetDlgItem(hdlg, IDC_DSNAMETEXT), FALSE);
}
return TRUE;
case WM_COMMAND:
switch (GET_WM_COMMAND_ID(wparam, lparam)) {
case IDC_BROWSE:
GetDBFile(hdlg);
break;
case IDOK:
#ifdef _WIN64
setupdlg = (SETUPDLG *) GetWindowLongPtr(hdlg, DWLP_USER);
#else
setupdlg = (SETUPDLG *) GetWindowLong(hdlg, DWL_USER);
#endif
GetDlgItemText(hdlg, IDC_DSNAME,
setupdlg->attr[KEY_DSN].attr,
sizeof (setupdlg->attr[KEY_DSN].attr));
GetDlgItemText(hdlg, IDC_DBNAME,
setupdlg->attr[KEY_DBNAME].attr,
sizeof (setupdlg->attr[KEY_DBNAME].attr));
GetDlgItemText(hdlg, IDC_TONAME,
setupdlg->attr[KEY_BUSY].attr,
sizeof (setupdlg->attr[KEY_BUSY].attr));
strcpy(setupdlg->attr[KEY_NOWCHAR].attr,
(IsDlgButtonChecked(hdlg, IDC_NOWCHAR) == BST_CHECKED) ?
"1" : "0");
strcpy(setupdlg->attr[KEY_STEPAPI].attr,
(IsDlgButtonChecked(hdlg, IDC_STEPAPI) == BST_CHECKED) ?
"1" : "0");
strcpy(setupdlg->attr[KEY_NOTXN].attr,
(IsDlgButtonChecked(hdlg, IDC_NOTXN) == BST_CHECKED) ?
"1" : "0");
strcpy(setupdlg->attr[KEY_LONGNAM].attr,
(IsDlgButtonChecked(hdlg, IDC_LONGNAM) == BST_CHECKED) ?
"1" : "0");
/* FALL THROUGH */
case IDCANCEL:
EndDialog(hdlg, GET_WM_COMMAND_ID(wparam, lparam) == IDOK);
return TRUE;
}
}
return FALSE;
}
#endif
#ifdef __OS2__
/**
* Dialog procedure for SQLDriverConnect().
* @param hdlg handle of dialog window
* @param wmsg type of message
* @param wparam wparam of message
* @param lparam lparam of message
* @result standard MRESULT
*/
static MRESULT EXPENTRY
DriverConnectProc(HWND hdlg, ULONG wmsg, MPARAM wparam, MPARAM lparam)
{
SETUPDLG *setupdlg;
switch (wmsg) {
case WM_INITDLG:
WinSetWindowULong(hdlg, QWL_USER, (ULONG) lparam);
setupdlg = (SETUPDLG *) lparam;
WinSetDlgItemText(hdlg, EF_DSNNAME, setupdlg->attr[KEY_DSN].attr);
WinSetDlgItemText(hdlg, EF_DSNDESC, setupdlg->attr[KEY_DESC].attr);
WinSetDlgItemText(hdlg, EF_DATABASE, setupdlg->attr[KEY_DBNAME].attr);
WinSetDlgItemText(hdlg, EF_TIMEOUT, setupdlg->attr[KEY_BUSY].attr);
WinCheckButton(hdlg, IDC_NOWCHAR,
getbool(setupdlg->attr[KEY_NOWCHAR].attr));
WinCheckButton(hdlg, IDC_STEPAPI,
getbool(setupdlg->attr[KEY_STEPAPI].attr));
WinCheckButton(hdlg, IDC_NOTXN,
getbool(setupdlg->attr[KEY_NOTXN].attr));
WinCheckButton(hdlg, IDC_LONGNAM,
getbool(setupdlg->attr[KEY_LONGNAM].attr));
return 0;
case WM_COMMAND:
switch (SHORT1FROMMP(wparam)) {
case IDC_BROWSE:
GetDBFile(hdlg);
return 0;
case DID_OK:
setupdlg = (SETUPDLG *) WinQueryWindowULong(hdlg, QWL_USER);
WinQueryDlgItemText(hdlg, EF_DSNNAME,
sizeof (setupdlg->attr[KEY_DSN].attr),
setupdlg->attr[KEY_DSN].attr);
WinQueryDlgItemText(hdlg, EF_DATABASE,
sizeof (setupdlg->attr[KEY_DBNAME].attr),
setupdlg->attr[KEY_DBNAME].attr);
WinQueryDlgItemText(hdlg, EF_TIMEOUT,
sizeof (setupdlg->attr[KEY_BUSY].attr),
setupdlg->attr[KEY_BUSY].attr);
if ((ULONG)WinSendDlgItemMsg(hdlg, IDC_NOWCHAR, BM_QUERYCHECK,
0, 0) == 1) {
strcpy(setupdlg->attr[KEY_NOWCHAR].attr, "1");
} else {
strcpy(setupdlg->attr[KEY_NOWCHAR].attr, "0");
}
if ((ULONG)WinSendDlgItemMsg(hdlg, IDC_STEPAPI, BM_QUERYCHECK,
0, 0) == 1) {
strcpy(setupdlg->attr[KEY_STEPAPI].attr, "1");
} else {
strcpy(setupdlg->attr[KEY_STEPAPI].attr, "0");
}
if ((ULONG)WinSendDlgItemMsg(hdlg, IDC_NOTXN, BM_QUERYCHECK,
0, 0) == 1) {
strcpy(setupdlg->attr[KEY_NOTXN].attr, "1");
} else {
strcpy(setupdlg->attr[KEY_NOTXN].attr, "0");
}
if ((ULONG)WinSendDlgItemMsg(hdlg, IDC_LONGNAM, BM_QUERYCHECK,
0, 0) == 1) {
strcpy(setupdlg->attr[KEY_LONGNAM].attr, "1");
} else {
strcpy(setupdlg->attr[KEY_LONGNAM].attr, "0");
}
WinDismissDlg(hdlg, DID_OK);
return 0;
case DID_CANCEL:
WinDismissDlg(hdlg, DID_CANCEL);
return 0;
}
}
return WinDefDlgProc(hdlg, wmsg, wparam, lparam);
}
#endif
/**
* Internal connect using a driver connection string.
* @param dbc database connection handle
* @param hwnd parent window handle
* @param connIn driver connect input string
* @param connInLen length of driver connect input string or SQL_NTS
* @param connOut driver connect output string
* @param connOutMax length of driver connect output string
* @param connOutLen output length of driver connect output string
* @param drvcompl completion type
* @result ODBC error code
*/
static SQLRETURN
drvdriverconnect(SQLHDBC dbc, SQLHWND hwnd,
SQLCHAR *connIn, SQLSMALLINT connInLen,
SQLCHAR *connOut, SQLSMALLINT connOutMax,
SQLSMALLINT *connOutLen, SQLUSMALLINT drvcompl)
{
BOOL maybeprompt, prompt = FALSE;
DBC *d;
SETUPDLG *setupdlg;
SQLRETURN ret;
if (dbc == SQL_NULL_HDBC) {
return SQL_INVALID_HANDLE;
}
d = (DBC *) dbc;
if (d->sqlite) {
setstatd(d, -1, "connection already established", "08002");
return SQL_ERROR;
}
setupdlg = (SETUPDLG *) xmalloc(sizeof (SETUPDLG));
if (setupdlg == NULL) {
return SQL_ERROR;
}
memset(setupdlg, 0, sizeof (SETUPDLG));
maybeprompt = drvcompl == SQL_DRIVER_COMPLETE ||
drvcompl == SQL_DRIVER_COMPLETE_REQUIRED;
if (connIn == NULL || !connInLen ||
(connInLen == SQL_NTS && !connIn[0])) {
prompt = TRUE;
} else {
ParseAttributes((LPCSTR) connIn, setupdlg);
if (!setupdlg->attr[KEY_DSN].attr[0] &&
drvcompl == SQL_DRIVER_COMPLETE_REQUIRED) {
strcpy(setupdlg->attr[KEY_DSN].attr, "DEFAULT");
}
GetAttributes(setupdlg);
if (drvcompl == SQL_DRIVER_PROMPT ||
(maybeprompt &&
!setupdlg->attr[KEY_DBNAME].attr[0])) {
prompt = TRUE;
}
}
retry:
if (prompt) {
short dlgret;
setupdlg->defDSN = setupdlg->attr[KEY_DRIVER].attr[0] != '\0';
#if defined(_WIN32) || defined(_WIN64)
dlgret = DialogBoxParam(hModule, MAKEINTRESOURCE(DRIVERCONNECT),
hwnd, (DLGPROC) DriverConnectProc,
(LPARAM) setupdlg);
#endif
#ifdef __OS2__
HMODULE hDLL;
DosQueryModuleHandle("SQLLODBC.DLL", &hDLL);
setupdlg->hDLL = hDLL;
dlgret = WinDlgBox(HWND_DESKTOP, (HWND) hwnd, DriverConnectProc, hDLL,
DLG_SQLITEODBCCONFIGURATION, setupdlg);
#endif
if (!dlgret || dlgret == -1) {
xfree(setupdlg);
return SQL_NO_DATA;
}
}
if (connOut || connOutLen) {
char buf[1024];
int len, count;
char dsn_0 = setupdlg->attr[KEY_DSN].attr[0];
char drv_0 = setupdlg->attr[KEY_DRIVER].attr[0];
buf[0] = '\0';
count = snprintf(buf, sizeof (buf),
"%s%s%s%s%s%sDatabase=%s;StepAPI=%s;"
"NoTXN=%s;Timeout=%s;NoWCHAR=%s;LongNames=%s",
dsn_0 ? "DSN=" : "",
dsn_0 ? setupdlg->attr[KEY_DSN].attr : "",
dsn_0 ? ";" : "",
drv_0 ? "Driver=" : "",
drv_0 ? setupdlg->attr[KEY_DRIVER].attr : "",
drv_0 ? ";" : "",
setupdlg->attr[KEY_DBNAME].attr,
setupdlg->attr[KEY_STEPAPI].attr,
setupdlg->attr[KEY_NOTXN].attr,
setupdlg->attr[KEY_BUSY].attr,
setupdlg->attr[KEY_NOWCHAR].attr,
setupdlg->attr[KEY_LONGNAM].attr);
if (count < 0) {
buf[sizeof (buf) - 1] = '\0';
}
len = min(connOutMax - 1, strlen(buf));
if (connOut) {
strncpy((char *) connOut, buf, len);
connOut[len] = '\0';
}
if (connOutLen) {
*connOutLen = len;
}
}
#if defined(HAVE_SQLITETRACE) && HAVE_SQLITETRACE
if (setupdlg->attr[KEY_DSN].attr[0]) {
char tracef[SQL_MAX_MESSAGE_LENGTH];
tracef[0] = '\0';
SQLGetPrivateProfileString(setupdlg->attr[KEY_DSN].attr,
"tracefile", "", tracef,
sizeof (tracef), ODBC_INI);
if (tracef[0] != '\0') {
d->trace = fopen(tracef, "a");
}
}
#endif
d->nowchar = getbool(setupdlg->attr[KEY_NOWCHAR].attr);
d->longnames = getbool(setupdlg->attr[KEY_LONGNAM].attr);
ret = dbopen(d, setupdlg->attr[KEY_DBNAME].attr,
setupdlg->attr[KEY_DSN].attr,
setupdlg->attr[KEY_STEPAPI].attr,
setupdlg->attr[KEY_NOTXN].attr,
setupdlg->attr[KEY_BUSY].attr);
if (ret != SQL_SUCCESS) {
if (maybeprompt && !prompt) {
prompt = TRUE;
goto retry;
}
xfree(setupdlg);
return ret;
}
xfree(setupdlg);
return SQL_SUCCESS;
}
#endif /* WITHOUT_DRIVERMGR */
#endif /* _WIN32 || _WIN64 || __OS2__ */
#ifndef WINTERFACE
/**
* Connect using a driver connection string.
* @param dbc database connection handle
* @param hwnd parent window handle
* @param connIn driver connect input string
* @param connInLen length of driver connect input string or SQL_NTS
* @param connOut driver connect output string
* @param connOutMax length of driver connect output string
* @param connOutLen output length of driver connect output string
* @param drvcompl completion type
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLDriverConnect(SQLHDBC dbc, SQLHWND hwnd,
SQLCHAR *connIn, SQLSMALLINT connInLen,
SQLCHAR *connOut, SQLSMALLINT connOutMax,
SQLSMALLINT *connOutLen, SQLUSMALLINT drvcompl)
{
SQLRETURN ret;
HDBC_LOCK(dbc);
ret = drvdriverconnect(dbc, hwnd, connIn, connInLen,
connOut, connOutMax, connOutLen, drvcompl);
HDBC_UNLOCK(dbc);
return ret;
}
#endif
#ifdef WINTERFACE
/**
* Connect using a driver connection string (UNICODE version).
* @param dbc database connection handle
* @param hwnd parent window handle
* @param connIn driver connect input string
* @param connInLen length of driver connect input string or SQL_NTS
* @param connOut driver connect output string
* @param connOutMax length of driver connect output string
* @param connOutLen output length of driver connect output string
* @param drvcompl completion type
* @result ODBC error code
*/
SQLRETURN SQL_API
SQLDriverConnectW(SQLHDBC dbc, SQLHWND hwnd,
SQLWCHAR *connIn, SQLSMALLINT connInLen,
SQLWCHAR *connOut, SQLSMALLINT connOutMax,
SQLSMALLINT *connOutLen, SQLUSMALLINT drvcompl)
{
SQLRETURN ret;
char *ci = NULL;
SQLSMALLINT len = 0;
HDBC_LOCK(dbc);
if (connIn) {
ci = uc_to_utf_c(connIn, connInLen);
if (!ci) {
DBC *d = (DBC *) dbc;
setstatd(d, -1, "out of memory", (*d->ov3) ? "HY000" : "S1000");
HDBC_UNLOCK(dbc);
return SQL_ERROR;
}
}
ret = drvdriverconnect(dbc, hwnd, (SQLCHAR *) ci, SQL_NTS,
(SQLCHAR *) connOut, connOutMax, &len, drvcompl);
HDBC_UNLOCK(dbc);
uc_free(ci);
if (ret == SQL_SUCCESS) {
SQLWCHAR *co = NULL;
if (connOut) {
if (len > 0) {
co = uc_from_utf((SQLCHAR *) connOut, len);
if (co) {
uc_strncpy(connOut, co, connOutMax);
co[len] = 0;
len = min(connOutMax, uc_strlen(co));
uc_free(co);
} else {
len = 0;
}
}
if (len <= 0) {
len = 0;
connOut[0] = 0;
}
} else {
len = 0;
}
if (connOutLen) {
*connOutLen = len;
}
}
return ret;
}
#endif
#if defined(_WIN32) || defined(_WIN64)
/**
* DLL initializer for WIN32.
* @param hinst instance handle
* @param reason reason code for entry point
* @param reserved
* @result always true
*/
BOOL APIENTRY
LibMain(HANDLE hinst, DWORD reason, LPVOID reserved)
{
static int initialized = 0;
switch (reason) {
case DLL_PROCESS_ATTACH:
if (!initialized++) {
hModule = hinst;
#ifdef WINTERFACE
/* MS Access hack part 1 (reserved error -7748) */
statSpec2P = statSpec2;
statSpec3P = statSpec3;
#endif
}
break;
case DLL_THREAD_ATTACH:
break;
case DLL_PROCESS_DETACH:
--initialized;
break;
case DLL_THREAD_DETACH:
break;
default:
break;
}
return TRUE;
}
/**
* DLL entry point for WIN32.
* @param hinst instance handle
* @param reason reason code for entry point
* @param reserved
* @result always true
*/
int __stdcall
DllMain(HANDLE hinst, DWORD reason, LPVOID reserved)
{
return LibMain(hinst, reason, reserved);
}
#ifndef WITHOUT_INSTALLER
/**
* Handler for driver installer/uninstaller error messages.
* @param name name of API function for which to show error messages
* @result true when error message retrieved
*/
static BOOL
InUnError(char *name)
{
WORD err = 1;
DWORD code;
char errmsg[301];
WORD errlen, errmax = sizeof (errmsg) - 1;
int sqlret;
BOOL ret = FALSE;
do {
errmsg[0] = '\0';
sqlret = SQLInstallerError(err, &code, errmsg, errmax, &errlen);
if (SQL_SUCCEEDED(sqlret)) {
MessageBox(NULL, errmsg, name,
MB_ICONSTOP|MB_OK|MB_TASKMODAL|MB_SETFOREGROUND);
ret = TRUE;
}
err++;
} while (sqlret != SQL_NO_DATA);
return ret;
}
/**
* Built in driver installer/uninstaller.
* @param remove true for uninstall
* @param cmdline command line string of rundll32
*/
static BOOL
InUn(int remove, char *cmdline)
{
#ifdef WINTERFACE
static char *drivername = "SQLite ODBC (UTF-8) Driver";
static char *dsname = "SQLite3 UTF-8 Datasource";
#else
static char *drivername = "SQLite3 ODBC Driver";
static char *dsname = "SQLite Datasource";
#endif
char *dllname, *p;
char dllbuf[301], path[301], driver[300], attr[300], inst[400];
WORD pathmax = sizeof (path) - 1, pathlen;
DWORD usecnt, mincnt;
int quiet = 0;
dllbuf[0] = '\0';
GetModuleFileName(hModule, dllbuf, sizeof (dllbuf));
p = strrchr(dllbuf, '\\');
dllname = p ? (p + 1) : dllbuf;
quiet = cmdline && strstr(cmdline, "quiet");
if (SQLInstallDriverManager(path, pathmax, &pathlen)) {
sprintf(driver, "%s;Driver=%s;Setup=%s;",
drivername, dllname, dllname);
p = driver;
while (*p) {
if (*p == ';') {
*p = '\0';
}
++p;
}
usecnt = 0;
path[0] = '\0';
SQLInstallDriverEx(driver, NULL, path, pathmax, NULL,
ODBC_INSTALL_INQUIRY, &usecnt);
pathlen = strlen(path);
while (pathlen > 0 && path[pathlen - 1] == '\\') {
--pathlen;
path[pathlen] = '\0';
}
sprintf(driver, "%s;Driver=%s\\%s;Setup=%s\\%s;",
drivername, path, dllname, path, dllname);
p = driver;
while (*p) {
if (*p == ';') {
*p = '\0';
}
++p;
}
sprintf(inst, "%s\\%s", path, dllname);
if (!remove && usecnt > 0) {
/* first install try: copy over driver dll, keeping DSNs */
if (GetFileAttributes(dllbuf) != 0xFFFFFFFF &&
CopyFile(dllbuf, inst, 0)) {
if (!quiet) {
char buf[512];
sprintf(buf, "%s replaced.", drivername);
MessageBox(NULL, buf, "Info",
MB_ICONINFORMATION|MB_OK|MB_TASKMODAL|
MB_SETFOREGROUND);
}
return TRUE;
}
}
mincnt = remove ? 1 : 0;
while (usecnt != mincnt) {
if (!SQLRemoveDriver(driver, TRUE, &usecnt)) {
break;
}
}
if (remove) {
if (usecnt && !SQLRemoveDriver(driver, TRUE, &usecnt)) {
InUnError("SQLRemoveDriver");
return FALSE;
}
if (!usecnt) {
char buf[512];
DeleteFile(inst);
if (!quiet) {
sprintf(buf, "%s uninstalled.", drivername);
MessageBox(NULL, buf, "Info",
MB_ICONINFORMATION|MB_OK|MB_TASKMODAL|
MB_SETFOREGROUND);
}
}
sprintf(attr, "DSN=%s;Database=sqlite.db;", dsname);
p = attr;
while (*p) {
if (*p == ';') {
*p = '\0';
}
++p;
}
SQLConfigDataSource(NULL, ODBC_REMOVE_SYS_DSN, drivername, attr);
return TRUE;
}
if (GetFileAttributes(dllbuf) == 0xFFFFFFFF) {
return FALSE;
}
if (strcmp(dllbuf, inst) != 0 && !CopyFile(dllbuf, inst, 0)) {
char buf[512];
sprintf(buf, "Copy %s to %s failed.", dllbuf, inst);
MessageBox(NULL, buf, "CopyFile",
MB_ICONSTOP|MB_OK|MB_TASKMODAL|MB_SETFOREGROUND);
return FALSE;
}
if (!SQLInstallDriverEx(driver, path, path, pathmax, &pathlen,
ODBC_INSTALL_COMPLETE, &usecnt)) {
InUnError("SQLInstallDriverEx");
return FALSE;
}
sprintf(attr, "DSN=%s;Database=sqlite.db;", dsname);
p = attr;
while (*p) {
if (*p == ';') {
*p = '\0';
}
++p;
}
SQLConfigDataSource(NULL, ODBC_REMOVE_SYS_DSN, drivername, attr);
if (!SQLConfigDataSource(NULL, ODBC_ADD_SYS_DSN, drivername, attr)) {
InUnError("SQLConfigDataSource");
return FALSE;
}
if (!quiet) {
char buf[512];
sprintf(buf, "%s installed.", drivername);
MessageBox(NULL, buf, "Info",
MB_ICONINFORMATION|MB_OK|MB_TASKMODAL|
MB_SETFOREGROUND);
}
} else {
InUnError("SQLInstallDriverManager");
return FALSE;
}
return TRUE;
}
/**
* RunDLL32 entry point for driver installation.
* @param hwnd window handle of caller
* @param hinst of this DLL
* @param lpszCmdLine rundll32 command line tail
* @param nCmdShow ignored
*/
void CALLBACK
install(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
{
InUn(0, lpszCmdLine);
}
/**
* RunDLL32 entry point for driver uninstallation.
* @param hwnd window handle of caller
* @param hinst of this DLL
* @param lpszCmdLine rundll32 command line tail
* @param nCmdShow ignored
*/
void CALLBACK
uninstall(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
{
InUn(1, lpszCmdLine);
}
#endif /* WITHOUT_INSTALLER */
#ifndef WITHOUT_SHELL
/**
* Setup argv vector from string
* @param argcp pointer to argc
* @param argvp pointer to argv
* @param cmdline command line string
* @param argv0 0th element for argv or NULL, must be static
*/
static void
setargv(int *argcp, char ***argvp, char *cmdline, char *argv0)
{
char *p, *arg, *argspace, **argv;
int argc, size, inquote, copy, slashes;
size = 2 + (argv0 ? 1 : 0);
for (p = cmdline; *p != '\0'; p++) {
if (ISSPACE(*p)) {
size++;
while (ISSPACE(*p)) {
p++;
}
if (*p == '\0') {
break;
}
}
}
argspace = malloc(size * sizeof (char *) + strlen(cmdline) + 1);
argv = (char **) argspace;
argspace += size * sizeof (char *);
size--;
argc = 0;
if (argv0) {
argv[argc++] = argv0;
}
p = cmdline;
for (; argc < size; argc++) {
argv[argc] = arg = argspace;
while (ISSPACE(*p)) {
p++;
}
if (*p == '\0') {
break;
}
inquote = 0;
slashes = 0;
while (1) {
copy = 1;
while (*p == '\\') {
slashes++;
p++;
}
if (*p == '"') {
if ((slashes & 1) == 0) {
copy = 0;
if (inquote && p[1] == '"') {
p++;
copy = 1;
} else {
inquote = !inquote;
}
}
slashes >>= 1;
}
while (slashes) {
*arg = '\\';
arg++;
slashes--;
}
if (*p == '\0' || (!inquote && ISSPACE(*p))) {
break;
}
if (copy != 0) {
*arg = *p;
arg++;
}
p++;
}
*arg = '\0';
argspace = arg + 1;
}
argv[argc] = 0;
*argcp = argc;
*argvp = argv;
}
/**
* RunDLL32 entry point for SQLite shell
* @param hwnd window handle of caller
* @param hinst of this DLL
* @param lpszCmdLine rundll32 command line tail
* @param nCmdShow ignored
*/
void CALLBACK
shell(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow)
{
int argc, needcon = 0;
char **argv;
extern int sqlite_main(int, char **);
static const char *name = "SQLite Shell";
DWORD ftype0, ftype1, ftype2;
ftype0 = GetFileType(GetStdHandle(STD_INPUT_HANDLE));
ftype1 = GetFileType(GetStdHandle(STD_OUTPUT_HANDLE));
ftype2 = GetFileType(GetStdHandle(STD_ERROR_HANDLE));
if (ftype0 != FILE_TYPE_DISK && ftype0 != FILE_TYPE_CHAR &&
ftype0 != FILE_TYPE_PIPE) {
fclose(stdin);
++needcon;
ftype0 = FILE_TYPE_UNKNOWN;
}
if (ftype1 != FILE_TYPE_DISK && ftype1 != FILE_TYPE_CHAR &&
ftype1 != FILE_TYPE_PIPE) {
fclose(stdout);
++needcon;
ftype1 = FILE_TYPE_UNKNOWN;
}
if (ftype2 != FILE_TYPE_DISK && ftype2 != FILE_TYPE_CHAR &&
ftype2 != FILE_TYPE_PIPE) {
fclose(stderr);
++needcon;
ftype2 = FILE_TYPE_UNKNOWN;
}
if (needcon > 0) {
AllocConsole();
SetConsoleTitle(name);
}
if (ftype0 == FILE_TYPE_UNKNOWN) {
freopen("CONIN$", "r", stdin);
}
if (ftype1 == FILE_TYPE_UNKNOWN) {
freopen("CONOUT$", "w", stdout);
}
if (ftype2 == FILE_TYPE_UNKNOWN) {
freopen("CONOUT$", "w", stderr);
}
setargv(&argc, &argv, lpszCmdLine, (char *) name);
sqlite_main(argc, argv);
}
#endif /* WITHOUT_SHELL */
#endif /* _WIN32 || _WIN64 */
#if defined(HAVE_ODBCINSTEXT_H) && HAVE_ODBCINSTEXT_H
/*
* unixODBC property page for this driver,
* may or may not work depending on unixODBC version.
*/
#include <odbcinstext.h>
int
ODBCINSTGetProperties(HODBCINSTPROPERTY prop)
{
static const char *instYN[] = { "No", "Yes", NULL };
prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY));
prop = prop->pNext;
memset(prop, 0, sizeof (ODBCINSTPROPERTY));
prop->nPromptType = ODBCINST_PROMPTTYPE_FILENAME;
strncpy(prop->szName, "Database", INI_MAX_PROPERTY_NAME);
strncpy(prop->szValue, "", INI_MAX_PROPERTY_VALUE);
prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY));
prop = prop->pNext;
memset(prop, 0, sizeof (ODBCINSTPROPERTY));
prop->nPromptType = ODBCINST_PROMPTTYPE_TEXTEDIT;
strncpy(prop->szName, "Timeout", INI_MAX_PROPERTY_NAME);
strncpy(prop->szValue, "100000", INI_MAX_PROPERTY_VALUE);
prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY));
prop = prop->pNext;
memset(prop, 0, sizeof (ODBCINSTPROPERTY));
prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX;
prop->aPromptData = malloc(sizeof (instYN));
memcpy(prop->aPromptData, instYN, sizeof (instYN));
strncpy(prop->szName, "StepAPI", INI_MAX_PROPERTY_NAME);
strncpy(prop->szValue, "No", INI_MAX_PROPERTY_VALUE);
#ifdef WINTERFACE
prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY));
prop = prop->pNext;
memset(prop, 0, sizeof (ODBCINSTPROPERTY));
prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX;
prop->aPromptData = malloc(sizeof (instYN));
memcpy(prop->aPromptData, instYN, sizeof (instYN));
strncpy(prop->szName, "NoWCHAR", INI_MAX_PROPERTY_NAME);
strncpy(prop->szValue, "No", INI_MAX_PROPERTY_VALUE);
#endif
prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY));
prop = prop->pNext;
memset(prop, 0, sizeof (ODBCINSTPROPERTY));
prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX;
prop->aPromptData = malloc(sizeof (instYN));
memcpy(prop->aPromptData, instYN, sizeof (instYN));
strncpy(prop->szName, "LongNames", INI_MAX_PROPERTY_NAME);
strncpy(prop->szValue, "No", INI_MAX_PROPERTY_VALUE);
return 1;
}
#endif /* HAVE_ODBCINSTEXT_H */