/** * @file sqlite3odbc.c * SQLite3 ODBC Driver main module. * * $Id: sqlite3odbc.c,v 1.110 2010/05/25 14:19:04 chw Exp chw $ * * Copyright (c) 2004-2010 Christian Werner * * See the file "license.terms" for information on usage * and redistribution of this file and for a * DISCLAIMER OF ALL WARRANTIES. */ #include "sqlite3odbc.h" #ifndef WITHOUT_WINTERFACE #define WINTERFACE #endif #ifdef WINTERFACE #include #endif #if defined(_WIN32) || defined(_WIN64) #include "resource3.h" #define ODBC_INI "ODBC.INI" #else #define ODBC_INI ".odbc.ini" #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 static const char *xdigits = "0123456789ABCDEFabcdef"; #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 #if defined(ENABLE_NVFS) && ENABLE_NVFS extern void nvfs_init(void); extern const char *nvfs_makevfs(const char *); #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) /* * Forward declarations of static functions. */ static void dbtraceapi(DBC *d, char *fn, const char *sql); static void freedyncols(STMT *s); static void freeresult(STMT *s, int clrcols); static void freerows(char **rowp); static void unbindcols(STMT *s); static void s3stmt_drop(STMT *s); static SQLRETURN drvexecute(SQLHSTMT stmt, int initial); static SQLRETURN freestmt(HSTMT stmt); static SQLRETURN mkbindcols(STMT *s, int ncols); static SQLRETURN setupdyncols(STMT *s, sqlite3_stmt *s3stmt, int *ncolsp); static SQLRETURN setupparbuf(STMT *s, BINDPARM *p); static SQLRETURN starttran(STMT *s); static SQLRETURN setupparam(STMT *s, char *sql, int pnum); #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); } #endif /* WINTERFACE */ #if defined(WINTERFACE) || defined(_WIN32) || defined(_WIN64) /** * Free converted UTF8 or UNICODE string. * @param str string to be free'd */ static void uc_free(void *str) { if (str) { xfree(str); } } #endif #if defined(_WIN32) || defined(_WIN64) /** * Convert multibyte, current code page string to UTF8 string, * @param str multibyte string to be converted * @param len length of multibyte string * @return alloc'ed UTF8 string to be free'd by uc_free() */ static char * wmb_to_utf(char *str, int len) { WCHAR *wstr; OSVERSIONINFO ovi; int nchar, is2k, cp = CP_OEMCP; ovi.dwOSVersionInfoSize = sizeof (ovi); GetVersionEx(&ovi); is2k = ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4; if (AreFileApisANSI()) { cp = is2k ? CP_THREAD_ACP : CP_ACP; } nchar = MultiByteToWideChar(cp, 0, str, len, NULL, 0); wstr = xmalloc((nchar + 1) * sizeof (WCHAR)); if (!wstr) { return NULL; } wstr[0] = 0; nchar = MultiByteToWideChar(cp, 0, str, len, wstr, nchar); wstr[nchar] = 0; str = xmalloc((nchar + 1) * 7); if (!str) { xfree(wstr); return NULL; } str[0] = '\0'; nchar = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, nchar * 7, 0, 0); str[nchar] = '\0'; xfree(wstr); return str; } /** * Convert UTF8 string to multibyte, current code page string, * @param str UTF8 string to be converted * @param len length of UTF8 string * @return alloc'ed multibyte string to be free'd by uc_free() */ static char * utf_to_wmb(char *str, int len) { WCHAR *wstr; OSVERSIONINFO ovi; int nchar, is2k, cp = CP_OEMCP; ovi.dwOSVersionInfoSize = sizeof (ovi); GetVersionEx(&ovi); is2k = ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4; if (AreFileApisANSI()) { cp = is2k ? CP_THREAD_ACP : CP_ACP; } nchar = MultiByteToWideChar(CP_UTF8, 0, str, len, NULL, 0); wstr = xmalloc((nchar + 1) * sizeof (WCHAR)); if (!wstr) { return NULL; } wstr[0] = 0; nchar = MultiByteToWideChar(CP_UTF8, 0, str, len, wstr, nchar); wstr[nchar] = 0; str = xmalloc((nchar + 1) * 7); if (!str) { xfree(wstr); return NULL; } str[0] = '\0'; nchar = WideCharToMultiByte(cp, 0, wstr, -1, str, nchar * 7, 0, 0); str[nchar] = '\0'; xfree(wstr); return str; } #ifdef WINTERFACE /** * Convert multibyte, current code page string to UNICODE string, * @param str multibyte string to be converted * @param len length of multibyte string * @return alloc'ed UNICODE string to be free'd by uc_free() */ static WCHAR * wmb_to_uc(char *str, int len) { WCHAR *wstr; OSVERSIONINFO ovi; int nchar, is2k, cp = CP_OEMCP; ovi.dwOSVersionInfoSize = sizeof (ovi); GetVersionEx(&ovi); is2k = ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4; if (AreFileApisANSI()) { cp = is2k ? CP_THREAD_ACP : CP_ACP; } nchar = MultiByteToWideChar(cp, 0, str, len, NULL, 0); wstr = xmalloc((nchar + 1) * sizeof (WCHAR)); if (!wstr) { return NULL; } wstr[0] = 0; nchar = MultiByteToWideChar(cp, 0, str, len, wstr, nchar); wstr[nchar] = 0; return wstr; } /** * Convert UNICODE string to multibyte, current code page string, * @param str UNICODE string to be converted * @param len length of UNICODE string * @return alloc'ed multibyte string to be free'd by uc_free() */ static char * uc_to_wmb(WCHAR *wstr, int len) { char *str; OSVERSIONINFO ovi; int nchar, is2k, cp = CP_OEMCP; ovi.dwOSVersionInfoSize = sizeof (ovi); GetVersionEx(&ovi); is2k = ovi.dwPlatformId == VER_PLATFORM_WIN32_NT && ovi.dwMajorVersion > 4; if (AreFileApisANSI()) { cp = is2k ? CP_THREAD_ACP : CP_ACP; } nchar = WideCharToMultiByte(cp, 0, wstr, len, NULL, 0, 0, 0); str = xmalloc((nchar + 1) * 2); if (!str) { return NULL; } str[0] = '\0'; nchar = WideCharToMultiByte(cp, 0, wstr, len, str, nchar * 2, 0, 0); str[nchar] = '\0'; return str; } #endif /* WINTERFACE */ #endif /* _WIN32 || _WIN64 */ #ifdef USE_DLOPEN_FOR_GPPS #include #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 #define drvgetgpps(d) #define drvrelgpps(d) #endif /* * Internal function to bind SQLite3 parameters. */ static void s3bind(DBC *d, sqlite3_stmt *stmt, int nparams, BINDPARM *p) { int i; if (stmt && p && nparams > 0) { for (i = 0; i < nparams; i++, p++) { switch (p->s3type) { default: case SQLITE_NULL: sqlite3_bind_null(stmt, i + 1); if (d->trace) { fprintf(d->trace, "-- parameter %d: NULL\n", i + 1); fflush(d->trace); } break; case SQLITE_TEXT: sqlite3_bind_text(stmt, i + 1, p->s3val, p->s3size, SQLITE_STATIC); if (d->trace) { fprintf(d->trace, "-- parameter %d: '%*s'\n", i + 1, p->s3size, (char *) p->s3val); fflush(d->trace); } break; case SQLITE_BLOB: sqlite3_bind_blob(stmt, i + 1, p->s3val, p->s3size, SQLITE_STATIC); if (d->trace) { fprintf(d->trace, "-- parameter %d: [BLOB]'\n", i + 1); fflush(d->trace); } break; case SQLITE_FLOAT: sqlite3_bind_double(stmt, i + 1, p->s3dval); if (d->trace) { fprintf(d->trace, "-- parameter %d: %g\n", i + 1, p->s3dval); fflush(d->trace); } break; case SQLITE_INTEGER: if (p->s3size > sizeof (int)) { sqlite3_bind_int64(stmt, i + 1, p->s3lival); if (d->trace) { fprintf(d->trace, #ifdef _WIN32 "-- parameter %d: %I64d\n", #else "-- parameter %d: %lld\n", #endif i + 1, p->s3lival); fflush(d->trace); } } else { sqlite3_bind_int(stmt, i + 1, p->s3ival); if (d->trace) { fprintf(d->trace, "-- parameter %d: %d\n", i + 1, p->s3ival); fflush(d->trace); } } break; } } } } /* * Internal structure for managing driver's * sqlite3_get_table() implementation. */ typedef struct tblres { char **resarr; char *errmsg; sqlite3_stmt *stmt; STMT *s; int nres; int nalloc; int nrow; int ncol; PTRDIFF_T ndata; int rc; } TBLRES; /* * Driver's version of sqlite3_get_table() and friends which are * capable of dealing with blobs. */ static int drvgettable_row(TBLRES *t, int ncol, int rc) { int need; int i; char *p; if (t->nrow == 0 && rc == SQLITE_ROW) { need = ncol * 2; } else { need = ncol; } if (t->ndata + need >= t->nalloc) { char **resnew; int nalloc = t->nalloc * 2 + need + 1; resnew = xrealloc(t->resarr, sizeof (char *) * nalloc); if (!resnew) { nomem: t->rc = SQLITE_NOMEM; return 1; } t->nalloc = nalloc; t->resarr = resnew; } /* column names when first row */ if (t->nrow == 0) { t->ncol = ncol; for (i = 0; i < ncol; i++) { p = (char *) sqlite3_column_name(t->stmt, i); if (p) { char *q = xmalloc(strlen(p) + 1); if (!q) { goto nomem; } strcpy(q, p); p = q; } t->resarr[t->ndata++] = p; } if (t->s && t->s->guessed_types) { int ncol2 = ncol; setupdyncols(t->s, t->stmt, &ncol2); t->s->guessed_types = 0; t->s->ncols = ncol; } } else if (t->ncol != ncol) { t->errmsg = sqlite3_mprintf("drvgettable() called with two or" " more incompatible queries"); t->rc = SQLITE_ERROR; return 1; } /* copy row data */ if (rc == SQLITE_ROW) { for (i = 0; i < ncol; i++) { int coltype = sqlite3_column_type(t->stmt, i); p = NULL; if (coltype == SQLITE_BLOB) { int k, nbytes = sqlite3_column_bytes(t->stmt, i); char *qp; unsigned const char *bp; bp = sqlite3_column_blob(t->stmt, i); qp = xmalloc(nbytes * 2 + 4); if (!qp) { goto nomem; } p = qp; *qp++ = 'X'; *qp++ = '\''; for (k = 0; k < nbytes; k++) { *qp++ = xdigits[(bp[k] >> 4)]; *qp++ = xdigits[(bp[k] & 0xF)]; } *qp++ = '\''; *qp = '\0'; } else if (coltype != SQLITE_NULL) { p = xstrdup((char *) sqlite3_column_text(t->stmt, i)); if (!p) { goto nomem; } } t->resarr[t->ndata++] = p; } t->nrow++; } return 0; } static int drvgettable(STMT *s, const char *sql, char ***resp, int *nrowp, int *ncolp, char **errp, int nparam, BINDPARM *p) { DBC *d = (DBC *) s->dbc; int rc = SQLITE_OK, keep = sql == NULL; TBLRES tres; const char *sqlleft = 0; int nretry = 0, haveerr = 0; if (!resp) { return SQLITE_ERROR; } *resp = NULL; if (nrowp) { *nrowp = 0; } if (ncolp) { *ncolp = 0; } tres.errmsg = NULL; tres.nres = 0; tres.nrow = 0; tres.ncol = 0; tres.ndata = 1; tres.nalloc = 20; tres.rc = SQLITE_OK; tres.resarr = xmalloc(sizeof (char *) * tres.nalloc); tres.stmt = NULL; tres.s = s; if (!tres.resarr) { return SQLITE_NOMEM; } tres.resarr[0] = 0; if (sql == NULL) { tres.stmt = s->s3stmt; if (tres.stmt == NULL) { return SQLITE_NOMEM; } goto retrieve; } while (sql && *sql && (rc == SQLITE_OK || (rc == SQLITE_SCHEMA && (++nretry) < 2))) { int ncol; tres.stmt = NULL; #if defined(HAVE_SQLITE3PREPAREV2) && HAVE_SQLITE3PREPAREV2 dbtraceapi(d, "sqlite3_prepare_v2", sql); rc = sqlite3_prepare_v2(d->sqlite, sql, -1, &tres.stmt, &sqlleft); #else dbtraceapi(d, "sqlite3_prepare", sql); rc = sqlite3_prepare(d->sqlite, sql, -1, &tres.stmt, &sqlleft); #endif if (rc != SQLITE_OK) { if (tres.stmt) { dbtraceapi(d, "sqlite3_finalize", 0); sqlite3_finalize(tres.stmt); tres.stmt = NULL; } continue; } if (!tres.stmt) { /* this happens for a comment or white-space */ sql = sqlleft; continue; } retrieve: if (sqlite3_bind_parameter_count(tres.stmt) != nparam) { if (errp) { *errp = sqlite3_mprintf("%s", "parameter marker count incorrect"); } haveerr = 1; rc = SQLITE_ERROR; goto tbldone; } s3bind(d, tres.stmt, nparam, p); ncol = sqlite3_column_count(tres.stmt); while (1) { if (s->max_rows && tres.nrow >= s->max_rows) { rc = SQLITE_OK; break; } rc = sqlite3_step(tres.stmt); if (rc == SQLITE_ROW || rc == SQLITE_DONE) { if (drvgettable_row(&tres, ncol, rc)) { rc = SQLITE_ABORT; goto tbldone; } } if (rc != SQLITE_ROW) { if (keep) { dbtraceapi(d, "sqlite3_reset", 0); rc = sqlite3_reset(tres.stmt); s->s3stmt_noreset = 1; } else { dbtraceapi(d, "sqlite3_finalize", 0); rc = sqlite3_finalize(tres.stmt); } tres.stmt = 0; if (rc != SQLITE_SCHEMA) { nretry = 0; sql = sqlleft; while (sql && ISSPACE(*sql)) { sql++; } } if (rc == SQLITE_DONE) { rc = SQLITE_OK; } break; } } } tbldone: if (tres.stmt) { if (keep) { if (!s->s3stmt_noreset) { dbtraceapi(d, "sqlite3_reset", 0); sqlite3_reset(tres.stmt); s->s3stmt_noreset = 1; } } else { dbtraceapi(d, "sqlite3_finalize", 0); sqlite3_finalize(tres.stmt); } } if (haveerr) { /* message already in *errp if any */ } else if (rc != SQLITE_OK && rc == sqlite3_errcode(d->sqlite) && errp) { *errp = sqlite3_mprintf("%s", sqlite3_errmsg(d->sqlite)); } else if (errp) { *errp = NULL; } if (tres.resarr) { tres.resarr[0] = (char *) (tres.ndata - 1); } if (rc == SQLITE_ABORT) { freerows(&tres.resarr[1]); if (tres.errmsg) { if (errp) { if (*errp) { sqlite3_free(*errp); } *errp = tres.errmsg; } else { sqlite3_free(tres.errmsg); } } return tres.rc; } sqlite3_free(tres.errmsg); if (rc != SQLITE_OK) { freerows(&tres.resarr[1]); return rc; } *resp = &tres.resarr[1]; if (ncolp) { *ncolp = tres.ncol; } if (nrowp) { *nrowp = tres.nrow; } return rc; } /** * 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 */ 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 } /** * 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 count count of subsequenct calls * @result true or false */ static int busy_handler(void *udata, 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; } /** * Set SQLite options (PRAGMAs) given SQLite handle. * @param x SQLite database handle * @param d DBC pointer * @result SQLite error code * * SQLite < 3.3.x and not shortnames DSN option: * "full_column_names" is always turned on and "short_column_names" * is always turned off, to get the table names in column labels. */ static int setsqliteopts(sqlite3 *x, DBC *d) { int count = 0, step = 0, max, rc = SQLITE_ERROR; #if defined(HAVE_SQLITE3COLUMNTABLENAME) && (HAVE_SQLITE3COLUMNTABLENAME) max = d->longnames ? 3 : 1; #else max = 3; #endif if (d->shortnames) { max = 3; } while (step < max) { if (step < 1) { rc = sqlite3_exec(x, "PRAGMA empty_result_callbacks = on;", NULL, NULL, NULL); if (rc == SQLITE_OK) { rc = sqlite3_exec(x, d->fksupport ? "PRAGMA foreign_keys = on;" : "PRAGMA foreign_keys = off;", NULL, NULL, NULL); } } else if (step < 2) { rc = sqlite3_exec(x, d->shortnames ? "PRAGMA full_column_names = off;" : "PRAGMA full_column_names = on;", NULL, NULL, NULL); } else if (step < 3) { rc = sqlite3_exec(x, d->shortnames ? "PRAGMA short_column_names = on;" : "PRAGMA short_column_names = off;", NULL, NULL, NULL); } if (rc != SQLITE_OK) { if (rc != SQLITE_BUSY || !busy_handler((void *) d, ++count)) { return rc; } continue; } count = 0; ++step; } sqlite3_busy_handler(x, busy_handler, (void *) d); 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) { PTRDIFF_T 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 #ifdef SQL_BIT } else if (strncmp(p, "bool", 4) == 0 || strncmp(p, "bit", 3) == 0) { result = SQL_BIT; #endif #ifdef SQL_BIGINT } else if (strncmp(p, "bigint", 6) == 0) { result = SQL_BIGINT; #endif } else if (strncmp(p, "blob", 4) == 0) { result = SQL_BINARY; } else if (strncmp(p, "varbinary", 9) == 0) { result = SQL_VARBINARY; } else if (strncmp(p, "longvarbinary", 13) == 0) { result = SQL_LONGVARBINARY; } 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 case SQL_VARBINARY: m = 255; d = 0; break; case SQL_LONGVARBINARY: m = 65536; d = 0; break; #ifdef SQL_BIGINT case SQL_BIGINT: m = 20; d = 19; 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 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 case SQL_BINARY: case SQL_VARBINARY: case SQL_LONGVARBINARY: type = SQL_C_BINARY; break; #ifdef SQL_BIT case SQL_BIT: type = SQL_C_BIT; break; #endif #ifdef SQL_BIGINT case SQL_BIGINT: type = (nosign > 0) ? SQL_C_UBIGINT : SQL_C_SBIGINT; 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 * @result newly allocated string containing query string for SQLite or NULL */ static char * fixupsql(char *sql, int sqlLen, int *nparam, int *isselect, char **errmsg) { char *q = sql, *qz = NULL, *p, *inq = NULL, *out; int np = 0, isddl = -1, size; *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(size); if (!p) { errout: freep(&qz); return NULL; } memset(p, 0, size); out = p; 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 '?': *p++ = *q; if (!inq) { np++; } 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 '{': /* 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); } } 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 d DBC pointer * * 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, DBC *d) { int i; #if !defined(HAVE_SQLITE3TABLECOLUMNMETADATA) || !HAVE_SQLITE3TABLECOLUMNMETADATA int k, pk, nn, t, r, nrows, ncols; char **rowp, *flagp, flags[128]; #endif 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; } } for (i = 0; i < s->dcols; i++) { s->dyncols[i].type = mapsqltype(s->dyncols[i].typename, &s->dyncols[i].nosign, *s->ov3, s->nowchar[0] || s->nowchar[1]); getmd(s->dyncols[i].typename, 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 (s->dyncols[i].type == SQL_VARBINARY && s->dyncols[i].size > 255) { s->dyncols[i].type = SQL_LONGVARBINARY; } } #if !defined(HAVE_SQLITE3TABLECOLUMNMETADATA) || !HAVE_SQLITE3TABLECOLUMNMETADATA 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; char *sql; if (!s->dyncols[i].table[0]) { continue; } if (flagp[i]) { continue; } sql = sqlite3_mprintf("PRAGMA table_info(%Q)", s->dyncols[i].table); if (!sql) { continue; } dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, NULL); sqlite3_free(sql); 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++) { char *colname = s->dyncols[m].column; if (s->longnames) { char *dotp = strchr(colname, '.'); if (dotp) { colname = dotp + 1; } } if (!flagp[m] && strcmp(colname, 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 (s->dyncols[i].type == SQL_VARBINARY && s->dyncols[i].size > 255) { s->dyncols[i].type = SQL_LONGVARBINARY; } 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: sqlite3_free_table(rowp); } if (flagp != flags) { freep(&flagp); } #endif } /** * 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; } /** * SQLite trace callback * @param arg DBC pointer * @param msg log message, SQL text */ 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 API calls * @param d pointer to database connection handle * @param fn SQLite function name * @param sql SQL string */ static void dbtraceapi(DBC *d, char *fn, const char *sql) { if (fn && d->trace) { if (sql) { fprintf(d->trace, "-- %s: %s\n", fn, sql); } else { fprintf(d->trace, "-- %s\n", fn); } fflush(d->trace); } } /** * Trace function for SQLite return codes * @param d pointer to database connection handle * @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); } } /** * Open SQLite database file given file name and flags. * @param d DBC pointer * @param name file name * @param isu true/false: file name is UTF8 encoded * @param dsn data source name * @param sflag STEPAPI flag * @param spflag SyncPragma string * @param ntflag NoTransaction string * @param jmode JournalMode string * @param busy busy/lock timeout * @result ODBC error code */ static SQLRETURN dbopen(DBC *d, char *name, int isu, char *dsn, char *sflag, char *spflag, char *ntflag, char *jmode, char *busy) { char *endp = NULL; int rc, tmp, busyto = 100000; #if defined(HAVE_SQLITE3VFS) && HAVE_SQLITE3VFS int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE; char *uname = name; const char *vfs_name = NULL; #endif if (d->sqlite) { if (d->trace) { fprintf(d->trace, "-- sqlite3_close (deferred): '%s'\n", d->dbname); fflush(d->trace); } sqlite3_close(d->sqlite); d->sqlite = NULL; } #if defined(HAVE_SQLITE3VFS) && HAVE_SQLITE3VFS if (d->nocreat) { flags &= ~ SQLITE_OPEN_CREATE; } #if defined(_WIN32) || defined(_WIN64) if (!isu) { uname = wmb_to_utf(name, -1); if (!uname) { rc = SQLITE_NOMEM; setstatd(d, rc, "out of memory", (*d->ov3) ? "HY000" : "S1000"); return SQL_ERROR; } } #endif #if defined(ENABLE_NVFS) && ENABLE_NVFS vfs_name = nvfs_makevfs(uname); #endif rc = sqlite3_open_v2(uname, &d->sqlite, flags, vfs_name); #if defined(WINTERFACE) || defined(_WIN32) || defined(_WIN64) if (uname != name) { uc_free(uname); } #endif #else #if defined(_WIN32) || defined(_WIN64) if (d->nocreat) { char *cname = NULL; if (isu) { cname = utf_to_wmb(name, -1); } if (GetFileAttributesA(cname ? cname : name) == 0xffffffff) { uc_free(cname); rc = SQLITE_CANTOPEN; setstatd(d, rc, "cannot open database", (*d->ov3) ? "HY000" : "S1000"); return SQL_ERROR; } uc_free(cname); } #else if (d->nocreat && access(name, 004) < 0) { rc = SQLITE_CANTOPEN; setstatd(d, rc, "cannot open database", (*d->ov3) ? "HY000" : "S1000"); return SQL_ERROR; } #endif #if defined(_WIN32) || defined(_WIN64) if (!isu) { WCHAR *wname = wmb_to_uc(name, -1); if (!wname) { rc = SQLITE_NOMEM; setstatd(d, rc, "out of memory", (*d->ov3) ? "HY000" : "S1000"); return SQL_ERROR; } rc = sqlite3_open16(wname, &d->sqlite); uc_free(wname); } else #endif rc = sqlite3_open(name, &d->sqlite); #endif /* !HAVE_SQLITE3VFS */ if (rc != SQLITE_OK) { connfail: setstatd(d, rc, "connect failed", (*d->ov3) ? "HY000" : "S1000"); if (d->sqlite) { sqlite3_close(d->sqlite); d->sqlite = NULL; } return SQL_ERROR; } if (d->trace) { sqlite3_trace(d->sqlite, dbtrace, d); } 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 ((rc = setsqliteopts(d->sqlite, d)) != SQLITE_OK) { if (d->trace) { fprintf(d->trace, "-- sqlite3_close: '%s'\n", d->dbname); fflush(d->trace); } sqlite3_close(d->sqlite); d->sqlite = NULL; goto connfail; } if (!spflag || spflag[0] == '\0') { spflag = "NORMAL"; } if (spflag[0] != '\0') { char syncp[128]; sprintf(syncp, "PRAGMA synchronous = %8.8s;", spflag); sqlite3_exec(d->sqlite, syncp, NULL, NULL, NULL); } if (jmode[0] != '\0') { char jourp[128]; sprintf(jourp, "PRAGMA journal_mode = %16.16s;", jmode); sqlite3_exec(d->sqlite, jourp, NULL, NULL, NULL); } freep(&d->dbname); d->dbname = xstrdup(name); freep(&d->dsn); d->dsn = xstrdup(dsn); if (d->trace) { fprintf(d->trace, "-- sqlite3_open: '%s'\n", d->dbname); fflush(d->trace); } #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; } /** * Load SQLite extension modules, if any * @param d DBC pointer * @param exts string, comma separated extension names */ static void dbloadext(DBC *d, char *exts) { #if defined(HAVE_SQLITE3LOADEXTENSION) && HAVE_SQLITE3LOADEXTENSION char *p; char path[SQL_MAX_MESSAGE_LENGTH]; int plen = 0; if (!d->sqlite) { return; } sqlite3_enable_load_extension(d->sqlite, 1); #if defined(_WIN32) || defined(_WIN64) GetModuleFileName(hModule, path, sizeof (path)); p = strrchr(path, '\\'); plen = p ? ((p + 1) - path) : 0; #endif do { p = strchr(exts, ','); if (p) { strncpy(path + plen, exts, p - exts); path[plen + (p - exts)] = '\0'; } else { strcpy(path + plen, exts); } if (exts[0]) { char *errmsg = NULL; int rc; #if defined(_WIN32) || defined(_WIN64) char *q; q = path + plen; if (!(q[0] && ((q[1] == ':' && (q[2] == '\\' || q[2] == '/')) || q[0] == '\\' || q[0] == '/' || q[0] == '.'))) { q = path; } rc = sqlite3_load_extension(d->sqlite, q, 0, &errmsg); #else rc = sqlite3_load_extension(d->sqlite, path, 0, &errmsg); #endif if (rc != SQLITE_OK) { #if defined(_WIN32) || defined(_WIN64) char buf[512], msg[512]; LoadString(hModule, IDS_EXTERR, buf, sizeof (buf)); wsprintf(msg, buf, q, errmsg ? errmsg : "no error info available"); LoadString(hModule, IDS_EXTTITLE, buf, sizeof (buf)); MessageBox(NULL, msg, buf, MB_ICONEXCLAMATION | MB_OK | MB_TASKMODAL | MB_SETFOREGROUND); #else fprintf(stderr, "extension '%s' did not load%s%s\n", path, errmsg ? ": " : "", errmsg ? errmsg : ""); #endif } } if (p) { exts = p + 1; } } while (p); #endif /* HAVE_SQLITE3LOADEXTENSION */ } /** * Find out column type * @param s3stmt SQLite statement pointer * @param col column number * @param d DBC pointer (for tracing only) * @param guessed_types flag array * @result type name as string */ static char * s3stmt_coltype(sqlite3_stmt *s3stmt, int col, DBC *d, int *guessed_types) { char *typename = (char *) sqlite3_column_decltype(s3stmt, col); char guess[64]; guess[0] = '\0'; if (!typename) { int coltype = sqlite3_column_type(s3stmt, col); if (guessed_types) { guessed_types[0]++; } if (d->trace) { sprintf(guess, " (guessed from %d)", coltype); } switch (coltype) { case SQLITE_INTEGER: typename = "integer"; break; case SQLITE_FLOAT: typename = "double"; break; default: case SQLITE_TEXT: typename = "varchar"; break; case SQLITE_BLOB: typename = "blob"; break; #if 0 case SQLITE_NULL: typename = "null"; break; #endif } } if (d->trace) { fprintf(d->trace, "-- column %d type%s: '%s'\n", col + 1, guess, typename); fflush(d->trace); } return typename; } #if defined(HAVE_SQLITE3TABLECOLUMNMETADATA) && HAVE_SQLITE3TABLECOLUMNMETADATA /** * Add meta data for column * @param s3stmt SQLite statement pointer * @param col column number * @param d DBC pointer (for tracing only) * @param ci pointer to COL */ static void s3stmt_addmeta(sqlite3_stmt *s3stmt, int col, DBC *d, COL *ci) { int nn = 0, pk = 0, ai = 0; const char *dn, *tn, *cn, *dummy1, *dummy2; dn = sqlite3_column_database_name(s3stmt, col); tn = sqlite3_column_table_name(s3stmt, col); cn = sqlite3_column_origin_name(s3stmt, col); sqlite3_table_column_metadata(d->sqlite, dn, tn, cn, &dummy1, &dummy2, &nn, &pk, &ai); ci->autoinc = ai ? SQL_TRUE: SQL_FALSE; ci->notnull = nn ? SQL_NO_NULLS : SQL_NULLABLE; if (d->trace) { fprintf(d->trace, "-- column %d %s\n", col + 1, nn ? "notnull" : "nullable"); if (ai) { fprintf(d->trace, "-- column %d autoincrement\n", col + 1); } fflush(d->trace); } } #endif /** * Do one sqlite statement step gathering one result row * @param s statement pointer * @result ODBC error code */ static int s3stmt_step(STMT *s) { DBC *d = (DBC *) s->dbc; char **rowd = NULL; const char *errp = NULL; int i, ncols, rc; if (s != d->cur_s3stmt || !s->s3stmt) { setstat(s, -1, "stale statement", (*s->ov3) ? "HY000" : "S1000"); return SQL_ERROR; } rc = sqlite3_step(s->s3stmt); if (rc == SQLITE_ROW || rc == SQLITE_DONE) { ++s->s3stmt_rownum; ncols = sqlite3_column_count(s->s3stmt); if (d->s3stmt_needmeta && s->s3stmt_rownum == 0 && ncols > 0) { PTRDIFF_T size; char *p; COL *dyncols; const char *colname, *typename; #if defined(HAVE_SQLITE3COLUMNTABLENAME) && (HAVE_SQLITE3COLUMNTABLENAME) char *tblname; #endif for (i = size = 0; i < ncols; i++) { colname = sqlite3_column_name(s->s3stmt, i); size += 3 + 3 * strlen(colname); } #if defined(HAVE_SQLITE3COLUMNTABLENAME) && (HAVE_SQLITE3COLUMNTABLENAME) tblname = (char *) size; for (i = 0; i < ncols; i++) { p = (char *) sqlite3_column_table_name(s->s3stmt, i); size += 2 + (p ? strlen(p) : 0); } #endif dyncols = xmalloc(ncols * sizeof (COL) + size); if (!dyncols) { freedyncols(s); s->ncols = 0; dbtraceapi(d, "sqlite3_finalize", 0); sqlite3_finalize(s->s3stmt); s->s3stmt = NULL; d->cur_s3stmt = NULL; return nomem(s); } p = (char *) (dyncols + ncols); #if defined(HAVE_SQLITE3COLUMNTABLENAME) && (HAVE_SQLITE3COLUMNTABLENAME) tblname = p + (PTRDIFF_T) tblname; #endif for (i = 0; i < ncols; i++) { char *q; colname = sqlite3_column_name(s->s3stmt, i); if (d->trace) { fprintf(d->trace, "-- column %d name: '%s'\n", i + 1, colname); fflush(d->trace); } #if defined(HAVE_SQLITE3COLUMNTABLENAME) && (HAVE_SQLITE3COLUMNTABLENAME) q = (char *) sqlite3_column_table_name(s->s3stmt, i); strcpy(tblname, q ? q : ""); if (d->trace) { fprintf(d->trace, "-- table %d name: '%s'\n", i + 1, tblname); fflush(d->trace); } dyncols[i].table = tblname; tblname += strlen(tblname) + 1; #endif typename = s3stmt_coltype(s->s3stmt, i, d, 0); dyncols[i].db = ((DBC *) (s->dbc))->dbname; strcpy(p, colname); dyncols[i].label = p; p += strlen(p) + 1; q = strchr(colname, '.'); if (q) { char *q2 = strchr(q + 1, '.'); /* SQLite 3.3.4 produces view.table.column sometimes */ if (q2) { q = q2; } } if (q) { #if !defined(HAVE_SQLITE3COLUMNTABLENAME) || !(HAVE_SQLITE3COLUMNTABLENAME) dyncols[i].table = p; #endif strncpy(p, colname, q - colname); p[q - colname] = '\0'; p += strlen(p) + 1; strcpy(p, q + 1); dyncols[i].column = p; p += strlen(p) + 1; } else { #if !defined(HAVE_SQLITE3COLUMNTABLENAME) || !(HAVE_SQLITE3COLUMNTABLENAME) dyncols[i].table = ""; #endif strcpy(p, colname); 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; #if defined(HAVE_SQLITE3TABLECOLUMNMETADATA) && HAVE_SQLITE3TABLECOLUMNMETADATA s3stmt_addmeta(s->s3stmt, i, d, &dyncols[i]); #else dyncols[i].autoinc = SQL_FALSE; dyncols[i].notnull = SQL_NULLABLE; #endif dyncols[i].typename = xstrdup(typename); } freedyncols(s); s->ncols = s->dcols = ncols; s->dyncols = s->cols = dyncols; fixupdyncols(s, d); mkbindcols(s, s->ncols); d->s3stmt_needmeta = 0; } if (ncols <= 0) { goto killstmt; } if (rc == SQLITE_DONE) { freeresult(s, 0); s->nrows = 0; dbtraceapi(d, "sqlite3_finalize", 0); sqlite3_finalize(s->s3stmt); s->s3stmt = NULL; d->cur_s3stmt = NULL; return SQL_SUCCESS; } rowd = xmalloc((1 + 2 * ncols) * sizeof (char *)); if (rowd) { const unsigned char *value; rowd[0] = (char *) ((PTRDIFF_T) (ncols * 2)); ++rowd; for (i = 0; i < ncols; i++) { int coltype = sqlite3_column_type(s->s3stmt, i); rowd[i] = rowd[i + ncols] = NULL; if (coltype == SQLITE_BLOB) { int k, nbytes = sqlite3_column_bytes(s->s3stmt, i); char *qp; unsigned const char *bp; bp = sqlite3_column_blob(s->s3stmt, i); qp = xmalloc(nbytes * 2 + 4); if (qp) { rowd[i + ncols] = qp; *qp++ = 'X'; *qp++ = '\''; for (k = 0; k < nbytes; k++) { *qp++ = xdigits[(bp[k] >> 4)]; *qp++ = xdigits[(bp[k] & 0xF)]; } *qp++ = '\''; *qp = '\0'; } } else if (coltype != SQLITE_NULL) { value = sqlite3_column_text(s->s3stmt, i); rowd[i + ncols] = xstrdup((char *) value); } } for (i = 0; i < ncols; i++) { int coltype = sqlite3_column_type(s->s3stmt, i); value = NULL; if (coltype == SQLITE_BLOB) { value = sqlite3_column_blob(s->s3stmt, i); } else if (coltype != SQLITE_NULL) { value = sqlite3_column_text(s->s3stmt, i); } if (value && !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) { dbtraceapi(d, "sqlite3_finalize", 0); sqlite3_finalize(s->s3stmt); s->s3stmt = NULL; d->cur_s3stmt = NULL; } return SQL_SUCCESS; } } killstmt: dbtraceapi(d, "sqlite3_reset", 0); rc = sqlite3_reset(s->s3stmt); s->s3stmt_noreset = 1; errp = sqlite3_errmsg(d->sqlite); if (d->cur_s3stmt == s) { d->cur_s3stmt = NULL; } setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", errp ? errp : "unknown error", rc); return SQL_ERROR; } /** * Stop running sqlite statement * @param s statement pointer */ static void s3stmt_end(STMT *s) { DBC *d; if (!s || !s->s3stmt) { return; } d = (DBC *) s->dbc; if (d) { d->busyint = 0; } if (!s->s3stmt_noreset) { dbtraceapi(d, "sqlite3_reset", 0); sqlite3_reset(s->s3stmt); s->s3stmt_noreset = 1; s->s3stmt_rownum = -1; } if (d->cur_s3stmt == s) { d->cur_s3stmt = NULL; } } /** * Conditionally stop running sqlite statement * @param s statement pointer */ static void s3stmt_end_if(STMT *s) { DBC *d = (DBC *) s->dbc; if (d) { d->busyint = 0; } if (d && d->cur_s3stmt == s) { s3stmt_end(s); } } /** * Drop running sqlite statement in STMT * @param s statement pointer */ static void s3stmt_drop(STMT *s) { if (s->s3stmt) { DBC *d = (DBC *) s->dbc; if (d) { dbtraceapi(d, "sqlite3_finalize", 0); } sqlite3_finalize(s->s3stmt); s->s3stmt = NULL; s->s3stmt_rownum = 0; } } /** * Start sqlite statement for execution of SELECT statement. * @param s statement pointer * @result ODBC error code */ static SQLRETURN s3stmt_start(STMT *s) { DBC *d = (DBC *) s->dbc; const char *endp; sqlite3_stmt *s3stmt = NULL; int rc, nretry = 0; d->s3stmt_needmeta = 0; if (!s->s3stmt) { #if defined(HAVE_SQLITE3PREPAREV2) && HAVE_SQLITE3PREPAREV2 dbtraceapi(d, "sqlite3_prepare_v2", (char *) s->query); #else dbtraceapi(d, "sqlite3_prepare", (char *) s->query); #endif do { s3stmt = NULL; #if defined(HAVE_SQLITE3PREPAREV2) && HAVE_SQLITE3PREPAREV2 rc = sqlite3_prepare_v2(d->sqlite, (char *) s->query, -1, &s3stmt, &endp); #else rc = sqlite3_prepare(d->sqlite, (char *) s->query, -1, &s3stmt, &endp); #endif if (rc != SQLITE_OK) { if (s3stmt) { sqlite3_finalize(s3stmt); s3stmt = NULL; } } } while (rc == SQLITE_SCHEMA && (++nretry) < 2); dbtracerc(d, rc, NULL); if (rc != SQLITE_OK) { if (s3stmt) { dbtraceapi(d, "sqlite3_finalize", NULL); sqlite3_finalize(s3stmt); } setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", sqlite3_errmsg(d->sqlite), rc); return SQL_ERROR; } if (sqlite3_bind_parameter_count(s3stmt) != s->nparams) { dbtraceapi(d, "sqlite3_finalize", 0); sqlite3_finalize(s3stmt); setstat(s, SQLITE_ERROR, "parameter marker count incorrect", (*s->ov3) ? "HY000" : "S1000"); return SQL_ERROR; } s->s3stmt = s3stmt; s->s3stmt_noreset = 1; d->s3stmt_needmeta = 1; } d->cur_s3stmt = s; s->s3stmt_rownum = -1; s3bind(d, s->s3stmt, s->nparams, s->bindparms); 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 /** * 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 && type != SQL_C_BINARY) { 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; #ifdef SQL_BIGINT case SQL_C_UBIGINT: case SQL_C_SBIGINT: size = sizeof (SQLBIGINT); break; #endif 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'; } p->need = (type == SQL_C_CHAR || type == SQL_C_WCHAR) ? -1 : 0; #else *((char *) p->param + p->len) = '\0'; p->need = (type == SQL_C_CHAR) ? -1 : 0; #endif #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 } } 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; } /** * Setup SQLite3 parameter for statement parameter. * @param s statement pointer * @param sql sql string * @param pnum parameter number * @result ODBC error code * * The parameter is converted within BINDPARM in order to * be presented to sqlite3_bind_*() functions. */ static SQLRETURN setupparam(STMT *s, char *sql, int pnum) { int type, len = 0, needalloc = 0; BINDPARM *p; 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)) { p->s3type = SQLITE_NULL; p->s3size = 0; return SQL_SUCCESS; } switch (type) { case SQL_C_BINARY: p->s3type = SQLITE_BLOB; p->s3size = p->len; p->s3val = p->param; if (p->need < 0) { break; } if (!p->lenp) { len = p->len; } else { len = *p->lenp; if (len <= SQL_LEN_DATA_AT_EXEC_OFFSET) { len = SQL_LEN_DATA_AT_EXEC(len); } } if (len < 0) { setstat(s, -1, "invalid length", "HY009"); return SQL_ERROR; } p->len = len; p->max = p->len; p->need = -1; p->s3size = len; break; #ifdef WINTERFACE case SQL_C_WCHAR: #endif case SQL_C_CHAR: p->s3type = SQLITE_TEXT; p->s3size = -1; p->s3val = p->param; 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 (p->need < 0 && p->parbuf == p->param) { break; } #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); p->s3val = p->param; p->s3size = p->len; } else #endif if (type == SQL_C_CHAR) { p->s3val = 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; p->s3val = p->param; p->s3size = p->len; } } break; case SQL_C_UTINYINT: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (int); p->s3ival = *((unsigned char *) p->param); break; case SQL_C_TINYINT: case SQL_C_STINYINT: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (int); p->s3ival = *((char *) p->param); break; case SQL_C_USHORT: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (int); p->s3ival = *((unsigned short *) p->param); break; case SQL_C_SHORT: case SQL_C_SSHORT: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (int); p->s3ival = *((short *) p->param); break; case SQL_C_ULONG: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (int); p->s3ival = *((unsigned int *) p->param); break; case SQL_C_LONG: case SQL_C_SLONG: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (int); p->s3ival = *((int *) p->param); break; #ifdef SQL_BIT case SQL_C_BIT: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (int); p->s3ival = (*((unsigned char *) p->param)) ? 1 : 0; break; #endif #ifdef SQL_BIGINT case SQL_C_SBIGINT: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (sqlite_int64); p->s3lival = *((sqlite_int64 *) p->param); break; case SQL_C_UBIGINT: p->s3type = SQLITE_INTEGER; p->s3size = sizeof (sqlite_int64); p->s3lival = *((sqlite_uint64 *) p->param); break; #endif case SQL_C_FLOAT: p->s3type = SQLITE_FLOAT; p->s3size = sizeof (double); p->s3dval = *((float *) p->param); break; case SQL_C_DOUBLE: p->s3type = SQLITE_FLOAT; p->s3size = sizeof (double); p->s3dval = *((double *) p->param); break; #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); p->s3type = SQLITE_TEXT; p->s3size = -1; p->s3val = p->strbuf; break; #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); p->s3type = SQLITE_TEXT; p->s3size = -1; p->s3val = p->strbuf; break; #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); p->s3type = SQLITE_TEXT; p->s3size = -1; p->s3val = p->strbuf; break; default: error: setstat(s, -1, "unsupported parameter type", (*s->ov3) ? "07009" : "S1093"); return SQL_ERROR; } 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->lenp = p->lenp0 = 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 nval 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; } s3stmt_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, *sql, 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); sql = sqlite3_mprintf("PRAGMA table_info(%Q)", tname); if (!sql) { return nomem(s); } sret = starttran(s); if (sret != SQL_SUCCESS) { sqlite3_free(sql); return sret; } dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, &errp); sqlite3_free(sql); if (ret != SQLITE_OK) { setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", errp ? errp : "unknown error", ret); if (errp) { sqlite3_free(errp); errp = NULL; } return SQL_ERROR; } if (errp) { sqlite3_free(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) { sql = sqlite3_mprintf("PRAGMA index_list(%Q)", tname); if (!sql) { sqlite3_free_table(rowp); return nomem(s); } dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowp2, &nrows2, &ncols2, &errp); sqlite3_free(sql); if (ret != SQLITE_OK) { sqlite3_free_table(rowp); sqlite3_free_table(rowp2); setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", errp ? errp : "unknown error", ret); if (errp) { sqlite3_free(errp); errp = NULL; } return SQL_ERROR; } if (errp) { sqlite3_free(errp); errp = NULL; } } if (ncols2 * nrows2 > 0) { namec2 = findcol(rowp2, ncols2, "name"); uniquec2 = findcol(rowp2, ncols2, "unique"); if (namec2 >= 0 && uniquec2 >= 0) { for (i = 1; i <= nrows2; i++) { int nnrows, nncols, nlen = 0; char **rowpp; if (rowp2[i * ncols2 + namec2]) { nlen = strlen(rowp2[i * ncols2 + namec2]); } if (nlen < 17 || strncmp(rowp2[i * ncols2 + namec2], "sqlite_autoindex_", 17)) { continue; } if (*rowp2[i * ncols2 + uniquec2] != '0') { ret = SQLITE_ERROR; sql = sqlite3_mprintf("PRAGMA index_info(%Q)", rowp2[i * ncols2 + namec2]); if (sql) { dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowpp, &nnrows, &nncols, NULL); sqlite3_free(sql); } if (ret == SQLITE_OK) { size += nnrows; sqlite3_free_table(rowpp); } } } } } if (size == 0) { sqlite3_free_table(rowp); sqlite3_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; sqlite3_free_table(rowp); sqlite3_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, nlen = 0; char **rowpp; if (rowp2[i * ncols2 + namec2]) { nlen = strlen(rowp2[i * ncols2 + namec2]); } if (nlen < 17 || strncmp(rowp2[i * ncols2 + namec2], "sqlite_autoindex_", 17)) { continue; } if (*rowp2[i * ncols2 + uniquec2] != '0') { int k; ret = SQLITE_ERROR; sql = sqlite3_mprintf("PRAGMA index_info(%Q)", rowp2[i * ncols2 + namec2]); if (sql) { dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowpp, &nnrows, &nncols, NULL); sqlite3_free(sql); } 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; sqlite3_free_table(rowpp); } } } sqlite3_free_table(rowp); sqlite3_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; PTRDIFF_T size; int namec = -1, uniquec = -1, namecc = -1, typecc = -1; int notnullcc = -1, mkrowid = 0; char *errp = NULL, *sql, 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; } sql = sqlite3_mprintf("PRAGMA index_list(%Q)", tname); if (!sql) { return nomem(s); } sret = starttran(s); if (sret != SQL_SUCCESS) { sqlite3_free(sql); return sret; } dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, &errp); sqlite3_free(sql); if (ret != SQLITE_OK) { doerr: setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", errp ? errp : "unknown error", ret); if (errp) { sqlite3_free(errp); errp = NULL; } return SQL_ERROR; } if (errp) { sqlite3_free(errp); errp = NULL; } size = 0; /* number result rows */ if (ncols * nrows <= 0) { goto nodata_but_rowid; } sql = sqlite3_mprintf("PRAGMA table_info(%Q)", tname); if (!sql) { return nomem(s); } dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowppp, &nnnrows, &nnncols, &errp); sqlite3_free(sql); if (ret != SQLITE_OK) { sqlite3_free_table(rowp); goto doerr; } if (errp) { sqlite3_free(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 = NULL; if (*rowp[i * ncols + uniquec] != '0') { ret = SQLITE_ERROR; sql = sqlite3_mprintf("PRAGMA index_info(%Q)", rowp[i * ncols + namec]); if (sql) { dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowpp, &nnrows, &nncols, NULL); sqlite3_free(sql); } if (ret == SQLITE_OK) { size += nnrows; sqlite3_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; sqlite3_free_table(rowp); sqlite3_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 = NULL; if (*rowp[i * ncols + uniquec] != '0') { int k; ret = SQLITE_ERROR; sql = sqlite3_mprintf("PRAGMA index_info(%Q)", rowp[i * ncols + namec]); if (sql) { dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowpp, &nnrows, &nncols, NULL); sqlite3_free(sql); } 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 (sqltype == SQL_VARBINARY && mm > 255) { sqltype = SQL_LONGVARBINARY; } 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; sqlite3_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: sqlite3_free_table(rowp); sqlite3_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; int onu, ond; PTRDIFF_T size; char **rowp, *errp = NULL, *sql, 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_ERROR; sql = sqlite3_mprintf("PRAGMA foreign_key_list(%Q)", fname); if (sql) { dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, &errp); sqlite3_free(sql); } if (ret != SQLITE_OK) { setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", errp ? errp : "unknown error", ret); if (errp) { sqlite3_free(errp); errp = NULL; } return SQL_ERROR; } if (errp) { sqlite3_free(errp); errp = NULL; } if (ncols * nrows <= 0) { nodata: sqlite3_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"); onu = findcol(rowp, ncols, "on_update"); ond = findcol(rowp, ncols, "on_delete"); 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); if (onu < 0) { s->rows[roffs + 9] = xstrdup(stringify(SQL_NO_ACTION)); } else { if (strcmp(rowp[i * ncols + onu], "SET NULL") == 0) { s->rows[roffs + 9] = xstrdup(stringify(SQL_SET_NULL)); } else if (strcmp(rowp[i * ncols + onu], "SET DEFAULT") == 0) { s->rows[roffs + 9] = xstrdup(stringify(SQL_SET_DEFAULT)); } else if (strcmp(rowp[i * ncols + onu], "CASCADE") == 0) { s->rows[roffs + 9] = xstrdup(stringify(SQL_CASCADE)); } else if (strcmp(rowp[i * ncols + onu], "RESTRICT") == 0) { s->rows[roffs + 9] = xstrdup(stringify(SQL_RESTRICT)); } else { s->rows[roffs + 9] = xstrdup(stringify(SQL_NO_ACTION)); } } if (ond < 0) { s->rows[roffs + 10] = xstrdup(stringify(SQL_NO_ACTION)); } else { if (strcmp(rowp[i * ncols + ond], "SET NULL") == 0) { s->rows[roffs + 10] = xstrdup(stringify(SQL_SET_NULL)); } else if (strcmp(rowp[i * ncols + ond], "SET DEFAULT") == 0) { s->rows[roffs + 10] = xstrdup(stringify(SQL_SET_DEFAULT)); } else if (strcmp(rowp[i * ncols + ond], "CASCADE") == 0) { s->rows[roffs + 10] = xstrdup(stringify(SQL_CASCADE)); } else if (strcmp(rowp[i * ncols + ond], "RESTRICT") == 0) { s->rows[roffs + 10] = xstrdup(stringify(SQL_RESTRICT)); } else { 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++; } sqlite3_free_table(rowp); } else { int nnrows, nncols, plen = strlen(pname); char **rowpp; sql = "select name from sqlite_master where type='table'"; dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, &errp); if (ret != SQLITE_OK) { setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", errp ? errp : "unknown error", ret); if (errp) { sqlite3_free(errp); errp = NULL; } return SQL_ERROR; } if (errp) { sqlite3_free(errp); errp = NULL; } if (ncols * nrows <= 0) { goto nodata; } size = 0; for (i = 1; i <= nrows; i++) { int k; if (!rowp[i]) { continue; } rowpp = NULL; ret = SQLITE_ERROR; sql = sqlite3_mprintf("PRAGMA foreign_key_list(%Q)", rowp[i]); if (sql) { dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowpp, &nnrows, &nncols, NULL); sqlite3_free(sql); } if (ret != SQLITE_OK || nncols * nnrows <= 0) { sqlite3_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) { sqlite3_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++; } sqlite3_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; } rowpp = NULL; ret = SQLITE_ERROR; sql = sqlite3_mprintf("PRAGMA foreign_key_list(%Q)", rowp[i]); if (sql) { dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowpp, &nnrows, &nncols, NULL); sqlite3_free(sql); } if (ret != SQLITE_OK || nncols * nnrows <= 0) { sqlite3_free_table(rowpp); continue; } namec = findcol(rowpp, nncols, "table"); seqc = findcol(rowpp, nncols, "seq"); fromc = findcol(rowpp, nncols, "from"); toc = findcol(rowpp, nncols, "to"); onu = findcol(rowpp, nncols, "on_update"); ond = findcol(rowpp, nncols, "on_delete"); if (namec < 0 || seqc < 0 || fromc < 0 || toc < 0) { sqlite3_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); if (onu < 0) { s->rows[roffs + 9] = xstrdup(stringify(SQL_NO_ACTION)); } else { if (strcmp(rowpp[k * nncols + onu], "SET NULL") == 0) { s->rows[roffs + 9] = xstrdup(stringify(SQL_SET_NULL)); } else if (strcmp(rowpp[k * nncols + onu], "SET DEFAULT") == 0) { s->rows[roffs + 9] = xstrdup(stringify(SQL_SET_DEFAULT)); } else if (strcmp(rowpp[k * nncols + onu], "CASCADE") == 0) { s->rows[roffs + 9] = xstrdup(stringify(SQL_CASCADE)); } else if (strcmp(rowpp[k * nncols + onu], "RESTRICT") == 0) { s->rows[roffs + 9] = xstrdup(stringify(SQL_RESTRICT)); } else { s->rows[roffs + 9] = xstrdup(stringify(SQL_NO_ACTION)); } } if (ond < 0) { s->rows[roffs + 10] = xstrdup(stringify(SQL_NO_ACTION)); } else { if (strcmp(rowpp[k * nncols + ond], "SET NULL") == 0) { s->rows[roffs + 10] = xstrdup(stringify(SQL_SET_NULL)); } else if (strcmp(rowpp[k * nncols + ond], "SET DEFAULT") == 0) { s->rows[roffs + 10] = xstrdup(stringify(SQL_SET_DEFAULT)); } else if (strcmp(rowpp[k * nncols + ond], "CASCADE") == 0) { s->rows[roffs + 10] = xstrdup(stringify(SQL_CASCADE)); } else if (strcmp(rowpp[k * nncols + ond], "RESTRICT") == 0) { s->rows[roffs + 10] = xstrdup(stringify(SQL_RESTRICT)); } else { 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++; } sqlite3_free_table(rowpp); } sqlite3_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, busy_count = 0; char *errp = NULL; DBC *d = (DBC *) s->dbc; if (!d->autocommit && !d->intrans && !d->trans_disable) { begin_again: rc = sqlite3_exec(d->sqlite, "BEGIN TRANSACTION", NULL, NULL, &errp); if (rc == SQLITE_BUSY) { if (busy_handler((void *) d, ++busy_count)) { if (errp) { sqlite3_free(errp); errp = NULL; } goto begin_again; } } 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) { sqlite3_free(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, busy_count = 0; 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: ret = sqlite3_exec(d->sqlite, sql, NULL, NULL, &errp); dbtracerc(d, ret, errp); if (ret == SQLITE_BUSY && !fail && comptype == SQL_COMMIT) { if (busy_handler((void *) d, ++busy_count)) { if (errp) { sqlite3_free(errp); errp = NULL; } goto doit; } } d->intrans = 0; if (ret != SQLITE_OK) { if (!fail) { setstatd(d, ret, "%s", (*d->ov3) ? "HY000" : "S1000", errp ? errp : "transaction failed"); if (errp) { sqlite3_free(errp); errp = NULL; } fail = 1; goto rollback; } if (errp) { sqlite3_free(errp); errp = NULL; } return SQL_ERROR; } if (errp) { sqlite3_free(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) e->owner = 0; LeaveCriticalSection(&e->cs); #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) { 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) { 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) { 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: 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; 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->s3stmt) { *uval = (s->s3stmt_rownum < 0) ? SQL_ROW_NUMBER_UNKNOWN : (s->s3stmt_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) = s->parm_bind_type; 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_MAX_ROWS: *((SQLUINTEGER *) val) = s->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_MAX_ROWS: s->max_rows = (PTRDIFF_T) val; return SQL_SUCCESS; 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; 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->s3stmt) { *ret = (s->s3stmt_rownum < 0) ? SQL_ROW_NUMBER_UNKNOWN : (s->s3stmt_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_MAX_ROWS: *ret = s->max_rows; return SQL_SUCCESS; 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_MAX_ROWS: s->max_rows = param; return SQL_SUCCESS; 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[] = "sqlite3odbc.so"; #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: *((SQLSMALLINT *) val) = 255; *valLen = sizeof (SQLSMALLINT); 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_FALSE; 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_SQLGETDIAGFIELD); 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_SQLGETDIAGFIELD: 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; #else #if defined(ENABLE_NVFS) && ENABLE_NVFS nvfs_init(); #endif #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) LeaveCriticalSection(&e->cs); e->owner = 0; #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; d->ov3 = &d->ov3val; verstr = sqlite3_libversion(); sscanf(verstr, "%d.%d.%d", &maj, &min, &lev); d->version = verinfo(maj & 0xFF, min & 0xFF, lev & 0xFF); 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 (d->trace) { fclose(d->trace); } 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_TRACE: case SQL_ATTR_TRACEFILE: case SQL_ATTR_QUIET_MODE: case SQL_ATTR_TRANSLATE_OPTION: case SQL_ATTR_KEYSET_SIZE: case SQL_ATTR_QUERY_TIMEOUT: case SQL_ATTR_CURRENT_CATALOG: *((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_ON; *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: *((SQLINTEGER *) val) = 0; *buflen = sizeof (SQLINTEGER); 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: *((SQLINTEGER *) val) = 0; *buflen = sizeof (SQLINTEGER); 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) { s3stmt_end(d->cur_s3stmt); } 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_TRACE: case SQL_OPT_TRACEFILE: case SQL_QUIET_MODE: case SQL_TRANSLATE_DLL: case SQL_TRANSLATE_OPTION: case SQL_KEYSET_SIZE: case SQL_QUERY_TIMEOUT: case SQL_BIND_TYPE: case SQL_CURRENT_QUALIFIER: *((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_ON; break; case SQL_CONCURRENCY: *((SQLINTEGER *) param) = SQL_CONCUR_LOCK; break; case SQL_SIMULATE_CURSOR: *((SQLINTEGER *) param) = SQL_SC_NON_UNIQUE; break; case SQL_MAX_ROWS: *((SQLINTEGER *) param) = 0; break; case SQL_ROWSET_SIZE: 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: *((SQLINTEGER *) param) = 0; 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) { s3stmt_end(d->cur_s3stmt); } 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)) /** * 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 * @param isu true/false: file name is UTF8 encoded * @result ODBC error code */ static SQLRETURN drvconnect(SQLHDBC dbc, SQLCHAR *dsn, SQLSMALLINT dsnLen, int isu) { DBC *d; int len; SQLRETURN ret; char buf[SQL_MAX_MESSAGE_LENGTH], dbname[SQL_MAX_MESSAGE_LENGTH / 4]; char busy[SQL_MAX_MESSAGE_LENGTH / 4], tracef[SQL_MAX_MESSAGE_LENGTH]; char loadext[SQL_MAX_MESSAGE_LENGTH]; char sflag[32], spflag[32], ntflag[32], nwflag[32]; char snflag[32], lnflag[32], ncflag[32], fkflag[32], jmode[32]; 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; } #if defined(_WIN32) || defined(_WIN64) /* * When DSN is in UTF it must be converted to ANSI * here for ANSI SQLGetPrivateProfileString() */ if (isu) { char *cdsn = utf_to_wmb(buf, len); if (!cdsn) { setstatd(d, -1, "out of memory", (*d->ov3) ? "HY000" : "S1000"); return SQL_ERROR; } strcpy(buf, cdsn); uc_free(cdsn); } #endif 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'; getdsnattr(buf, "stepapi", sflag, sizeof (sflag)); spflag[0] = '\0'; getdsnattr(buf, "syncpragma", spflag, sizeof (spflag)); ntflag[0] = '\0'; getdsnattr(buf, "notxn", ntflag, sizeof (ntflag)); nwflag[0] = '\0'; getdsnattr(buf, "nowchar", nwflag, sizeof (nwflag)); snflag[0] = '\0'; getdsnattr(buf, "shortnames", snflag, sizeof (snflag)); lnflag[0] = '\0'; getdsnattr(buf, "longnames", lnflag, sizeof (lnflag)); ncflag[0] = '\0'; getdsnattr(buf, "nocreat", ncflag, sizeof (ncflag)); fkflag[0] = '\0'; getdsnattr(buf, "fksupport", fkflag, sizeof (fkflag)); loadext[0] = '\0'; getdsnattr(buf, "loadext", loadext, sizeof (loadext)); jmode[0] = '\0'; getdsnattr(buf, "journalmode", jmode, sizeof (jmode)); #else SQLGetPrivateProfileString(buf, "timeout", "100000", busy, sizeof (busy), ODBC_INI); SQLGetPrivateProfileString(buf, "database", "", dbname, sizeof (dbname), ODBC_INI); #if defined(_WIN32) || defined(_WIN64) /* database name read from registry is not UTF8 !!! */ isu = 0; #endif SQLGetPrivateProfileString(buf, "stepapi", "", sflag, sizeof (sflag), ODBC_INI); SQLGetPrivateProfileString(buf, "syncpragma", "NORMAL", spflag, sizeof (spflag), ODBC_INI); SQLGetPrivateProfileString(buf, "notxn", "", ntflag, sizeof (ntflag), ODBC_INI); SQLGetPrivateProfileString(buf, "nowchar", "", nwflag, sizeof (nwflag), ODBC_INI); SQLGetPrivateProfileString(buf, "shortnames", "", snflag, sizeof (snflag), ODBC_INI); SQLGetPrivateProfileString(buf, "longnames", "", lnflag, sizeof (lnflag), ODBC_INI); SQLGetPrivateProfileString(buf, "nocreat", "", ncflag, sizeof (ncflag), ODBC_INI); SQLGetPrivateProfileString(buf, "fksupport", "", fkflag, sizeof (fkflag), ODBC_INI); SQLGetPrivateProfileString(buf, "loadext", "", loadext, sizeof (loadext), ODBC_INI); SQLGetPrivateProfileString(buf, "journalmode", "", jmode, sizeof (jmode), ODBC_INI); #endif 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"); } d->nowchar = getbool(nwflag); d->shortnames = getbool(snflag); d->longnames = getbool(lnflag); d->nocreat = getbool(ncflag); d->fksupport = getbool(fkflag); ret = dbopen(d, dbname, isu, (char *) dsn, sflag, spflag, ntflag, jmode, busy); if (ret == SQL_SUCCESS) { dbloadext(d, loadext); } return ret; } #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, 0); 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"); ret = SQL_ERROR; goto done; } } ret = drvconnect(dbc, (SQLCHAR *) dsna, SQL_NTS, 1); done: 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->cur_s3stmt) { s3stmt_end(d->cur_s3stmt); } if (d->sqlite) { if (d->trace) { fprintf(d->trace, "-- sqlite3_close: '%s'\n", d->dbname); fflush(d->trace); } sqlite3_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)) /** * 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; SQLRETURN ret; char buf[SQL_MAX_MESSAGE_LENGTH * 2], dbname[SQL_MAX_MESSAGE_LENGTH / 4]; char dsn[SQL_MAX_MESSAGE_LENGTH / 4], busy[SQL_MAX_MESSAGE_LENGTH / 4]; char tracef[SQL_MAX_MESSAGE_LENGTH], loadext[SQL_MAX_MESSAGE_LENGTH]; char sflag[32], spflag[32], ntflag[32], snflag[32], lnflag[32]; char ncflag[32], nwflag[32], fkflag[32], jmode[32]; 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 spflag[0] = '\0'; getdsnattr(buf, "syncpragma", spflag, sizeof (spflag)); #ifndef WITHOUT_DRIVERMGR if (dsn[0] && !spflag[0]) { SQLGetPrivateProfileString(dsn, "syncpragma", "NORMAL", spflag, sizeof (spflag), 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 snflag[0] = '\0'; getdsnattr(buf, "shortnames", snflag, sizeof (snflag)); #ifndef WITHOUT_DRIVERMGR if (dsn[0] && !snflag[0]) { SQLGetPrivateProfileString(dsn, "shortnames", "", snflag, sizeof (snflag), 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 ncflag[0] = '\0'; getdsnattr(buf, "nocreat", ncflag, sizeof (ncflag)); #ifndef WITHOUT_DRIVERMGR if (dsn[0] && !ncflag[0]) { SQLGetPrivateProfileString(dsn, "nocreat", "", ncflag, sizeof (ncflag), ODBC_INI); } #endif nwflag[0] = '\0'; getdsnattr(buf, "nowchar", nwflag, sizeof (nwflag)); #ifndef WITHOUT_DRIVERMGR if (dsn[0] && !nwflag[0]) { SQLGetPrivateProfileString(dsn, "nowchar", "", nwflag, sizeof (nwflag), ODBC_INI); } #endif fkflag[0] = '\0'; getdsnattr(buf, "fksupport", fkflag, sizeof (fkflag)); #ifndef WITHOUT_DRIVERMGR if (dsn[0] && !fkflag[0]) { SQLGetPrivateProfileString(dsn, "fksupport", "", fkflag, sizeof (fkflag), ODBC_INI); } #endif loadext[0] = '\0'; getdsnattr(buf, "loadext", loadext, sizeof (loadext)); #ifndef WITHOUT_DRIVERMGR if (dsn[0] && !loadext[0]) { SQLGetPrivateProfileString(dsn, "loadext", "", loadext, sizeof (loadext), ODBC_INI); } #endif jmode[0] = '\0'; getdsnattr(buf, "journalmode", jmode, sizeof (jmode)); #ifndef WITHOUT_DRIVERMGR if (dsn[0] && !jmode[0]) { SQLGetPrivateProfileString(dsn, "journalmode", "", jmode, sizeof (jmode), ODBC_INI); } #endif if (!dbname[0] && !dsn[0]) { strcpy(dsn, "SQLite"); strncpy(dbname, buf, sizeof (dbname)); dbname[sizeof (dbname) - 1] = '\0'; } 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 if (connOut || connOutLen) { int count; buf[0] = '\0'; count = snprintf(buf, sizeof (buf), "DSN=%s;Database=%s;StepAPI=%s;Timeout=%s;" "SyncPragma=%s;NoTXN=%s;ShortNames=%s;LongNames=%s;" "NoCreat=%s;NoWCHAR=%s;FKSupport=%s;Tracefile=%s;" "JournalMode=%s;LoadExt=%s", dsn, dbname, sflag, busy, spflag, ntflag, snflag, lnflag, ncflag, nwflag, fkflag, tracef, jmode, loadext); 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 (tracef[0] != '\0') { d->trace = fopen(tracef, "a"); } d->shortnames = getbool(snflag); d->longnames = getbool(lnflag); d->nocreat = getbool(ncflag); d->nowchar = getbool(nwflag); d->fksupport = getbool(fkflag); ret = dbopen(d, dbname, 0, dsn, sflag, spflag, ntflag, jmode, busy); if (ret == SQL_SUCCESS) { dbloadext(d, loadext); } return ret; } #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; s3stmt_drop(s); 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->curtype = d->curtype; s->row_status0 = &s->row_status1; s->rowset_size = 1; s->longnames = d->longnames; s->retr_data = SQL_RD_ON; s->max_rows = 0; 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_%I64X", (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: s3stmt_end_if(s); freeresult(s, 0); break; case SQL_DROP: s3stmt_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; sqlite3_interrupt(d->sqlite); } Sleep(1); } #else if (d->magic == DBC_MAGIC) { d->busyint = 1; sqlite3_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) { freep(&s->bincache); s->bincell = NULL; s->binlen = 0; 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, SQLINTEGER 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 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; #ifdef SQL_BIGINT case SQL_C_SBIGINT: case SQL_C_UBIGINT: *((SQLBIGINT *) val) = 0; break; #endif case SQL_C_FLOAT: *((float *) val) = 0; break; case SQL_C_DOUBLE: *((double *) val) = 0; break; case SQL_C_BINARY: case SQL_C_CHAR: *((char *) val) = '\0'; break; #ifdef WINTERFACE case SQL_C_WCHAR: *((SQLWCHAR *) val) = '\0'; break; #endif #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; #if defined(_WIN32) || defined(_WIN64) #ifdef SQL_BIGINT char endc; #endif #endif 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; #ifdef SQL_BIGINT case SQL_C_UBIGINT: #if defined(_WIN32) || defined(_WIN64) if (sscanf(*data, "%I64u%c", (SQLUBIGINT *) val, &endc) != 1) { *lenp = SQL_NULL_DATA; } else { *lenp = sizeof (SQLUBIGINT); } #else #ifdef __osf__ *((SQLUBIGINT *) val) = strtoul(*data, &endp, 0); #else *((SQLUBIGINT *) val) = strtoull(*data, &endp, 0); #endif if (endp && endp == *data) { *lenp = SQL_NULL_DATA; } else { *lenp = sizeof (SQLUBIGINT); } #endif break; case SQL_C_SBIGINT: #if defined(_WIN32) || defined(_WIN64) if (sscanf(*data, "%I64d%c", (SQLBIGINT *) val, &endc) != 1) { *lenp = SQL_NULL_DATA; } else { *lenp = sizeof (SQLBIGINT); } #else #ifdef __osf__ *((SQLBIGINT *) val) = strtol(*data, &endp, 0); #else *((SQLBIGINT *) val) = strtoll(*data, &endp, 0); #endif if (endp && endp == *data) { *lenp = SQL_NULL_DATA; } else { *lenp = sizeof (int); } #endif break; #endif 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: { int dlen, offs = 0; char *bin; if (valnull) { freep(&s->bincache); s->binlen = 0; goto doCHAR; } if (*data == s->bincell) { if (s->bincache) { bin = s->bincache; dlen = s->binlen; } else { goto doCHAR; } } else { char *dp; int i; freep(&s->bincache); dp = *data; dlen = strlen(dp); s->bincell = dp; s->binlen = 0; if (!(dp[0] == 'x' || dp[0] == 'X') || dp[1] != '\'' || dp[dlen - 1] != '\'') { goto doCHAR; } dlen -= 2; dp += 2; dlen = dlen / 2; s->bincache = bin = xmalloc(dlen); if (!bin) { return nomem(s); } s->binlen = dlen; memset(s->bincache, 0, dlen); for (i = 0; i < dlen; i++) { char *x; int v; if (!*dp || !(x = strchr(xdigits, *dp))) { goto converr; } v = x - xdigits; bin[i] = (v >= 16) ? ((v - 6) << 4) : (v << 4); ++dp; if (!*dp || !(x = strchr(xdigits, *dp))) { converr: freep(&s->bincache); s->binlen = 0; setstat(s, -1, "conversion error", (*s->ov3) ? "HY000" : "S1000"); return SQL_ERROR; } v = x - xdigits; bin[i] |= (v >= 16) ? (v - 6) : v; ++dp; } bin = s->bincache; } 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 && len) { memcpy(val, bin + offs, min(len, dlen)); } if (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; } doCHAR: #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; } /** * Interal 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 case SQL_C_BINARY: break; #ifdef SQL_BIGINT case SQL_C_SBIGINT: case SQL_C_UBIGINT: sz = sizeof (SQLBIGINT); 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, *sql, 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); #if defined(_WIN32) || defined(_WIN64) sql = sqlite3_mprintf("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", d->xcelqrx ? "''" : "NULL", d->xcelqrx ? "'main'" : "NULL", where, npatt ? "like" : "=", tname); #else sql = sqlite3_mprintf("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", where, npatt ? "like" : "=", tname); #endif if (!sql) { return nomem(s); } ret = starttran(s); if (ret != SQL_SUCCESS) { sqlite3_free(sql); return ret; } dbtraceapi(d, "sqlite3_get_table", sql); rc = sqlite3_get_table(d->sqlite, sql, &s->rows, &s->nrows, &ncols, &errp); sqlite3_free(sql); if (rc == SQLITE_OK) { if (ncols != s->ncols) { freeresult(s, 0); s->nrows = 0; } else { s->rowfree = sqlite3_free_table; } } else { s->nrows = 0; s->rows = NULL; s->rowfree = NULL; } if (errp) { sqlite3_free(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, *sql, 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'; } sql = sqlite3_mprintf("select tbl_name from sqlite_master where " "(type = 'table' or type = 'view') " "and tbl_name %s %Q", npatt ? "like" : "=", tname); if (!sql) { return nomem(s); } sret = starttran(s); if (sret != SQL_SUCCESS) { sqlite3_free(sql); return sret; } dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &trows, &tnrows, &tncols, &errp); sqlite3_free(sql); if (ret != SQLITE_OK) { setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", errp ? errp : "unknown error", ret); if (errp) { sqlite3_free(errp); errp = NULL; } return SQL_ERROR; } if (errp) { sqlite3_free(errp); errp = NULL; } /* pass 1: compute number of rows of result set */ if (tncols * tnrows <= 0) { sqlite3_free_table(trows); return SQL_SUCCESS; } size = 0; for (i = 1; i <= tnrows; i++) { sql = sqlite3_mprintf("PRAGMA table_info(%Q)", trows[i]); if (!sql) { sqlite3_free_table(trows); return nomem(s); } dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, &errp); sqlite3_free(sql); if (ret != SQLITE_OK) { setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", errp ? errp : "unknown error", ret); if (errp) { sqlite3_free(errp); errp = NULL; } sqlite3_free_table(trows); return SQL_ERROR; } if (errp) { sqlite3_free(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]) { if (namec >= 0) { for (k = 1; k <= nrows; k++) { if (namematch(rowp[k * ncols + namec], cname, 1)) { size++; } } } } else { size += nrows; } } sqlite3_free_table(rowp); } /* pass 2: fill result set */ if (size <= 0) { sqlite3_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; sqlite3_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++) { sql = sqlite3_mprintf("PRAGMA table_info(%Q)", trows[i]); if (!sql) { sqlite3_free_table(trows); return nomem(s); } dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, &errp); sqlite3_free(sql); if (ret != SQLITE_OK) { setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", errp ? errp : "unknown error", ret); if (errp) { sqlite3_free(errp); errp = NULL; } sqlite3_free_table(trows); return SQL_ERROR; } if (errp) { sqlite3_free(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; if (namec >= 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 = k; 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 = unquote(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 (sqltype == SQL_VARBINARY && mm > 255) { sqltype = SQL_LONGVARBINARY; } 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; } sqlite3_free_table(rowp); } sqlite3_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 #ifdef SQL_BIT case SQL_BIT: sign = NULL; s->rows[offs + 2] = "1"; 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; #ifdef SQL_BIGINT case SQL_BIGINT: s->rows[offs + 2] = "19"; break; #endif 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; case SQL_VARBINARY: sign = NULL; s->rows[offs + 2] = "255"; break; case SQL_LONGVARBINARY: sign = NULL; s->rows[offs + 2] = "65536"; break; } 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 SQL_LONGVARCHAR s->nrows = (sqltype == SQL_ALL_TYPES) ? 13 : 1; #else s->nrows = (sqltype == SQL_ALL_TYPES) ? 12 : 1; #endif if (sqltype == SQL_ALL_TYPES) { #ifdef WINTERFACE s->nrows += 2; #ifdef SQL_WLONGVARCHAR s->nrows += 2; #endif #endif } if (sqltype == SQL_ALL_TYPES) { s->nrows += 2; #ifdef SQL_BIT s->nrows += 1; #endif #ifdef SQL_BIGINT 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 mktypeinfo(s, cc++, asize, "varbinary", SQL_VARBINARY, 0); mktypeinfo(s, cc++, asize, "longvarbinary", SQL_LONGVARBINARY, 0); #ifdef SQL_BIT mktypeinfo(s, cc++, asize, "bit", SQL_BIT, 0); #endif #ifdef SQL_BIGINT mktypeinfo(s, cc++, asize, "bigint", SQL_BIGINT, 0); #endif #ifdef WINTERFACE mktypeinfo(s, cc++, asize, "wvarchar", SQL_WVARCHAR, 0); mktypeinfo(s, cc++, asize, "wchar", SQL_WCHAR, 0); #ifdef SQL_WLONGVARCHAR mktypeinfo(s, cc++, asize, "wtext", SQL_WLONGVARCHAR, 0); mktypeinfo(s, cc++, asize, "longwvarchar", SQL_WLONGVARCHAR, 0); #endif #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 case SQL_VARBINARY: mktypeinfo(s, 1, asize, "varbinary", SQL_VARBINARY, 30); break; case SQL_LONGVARBINARY: mktypeinfo(s, 1, asize, "longvarbinary", SQL_LONGVARBINARY, 31); break; #ifdef SQL_BIT case SQL_BIT: mktypeinfo(s, 1, asize, "bit", SQL_BIT, 29); break; #endif #ifdef SQL_BIGINT case SQL_BIGINT: mktypeinfo(s, 1, asize, "bigint", SQL_BIGINT, 28); 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 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, *sql, 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) { rowp = 0; ret = SQLITE_ERROR; sql = sqlite3_mprintf("PRAGMA table_info(%Q)", tname); if (sql) { dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, NULL); sqlite3_free(sql); } if (ret == SQLITE_OK) { int colid, typec, npk = 0; 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: sqlite3_free_table(rowp); } sql = sqlite3_mprintf("PRAGMA index_list(%Q)", tname); if (!sql) { return nomem(s); } dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowp, &nrows, &ncols, &errp); sqlite3_free(sql); if (ret != SQLITE_OK) { setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", errp ? errp : "unknown error", ret); if (errp) { sqlite3_free(errp); errp = NULL; } return SQL_ERROR; } if (errp) { sqlite3_free(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_ERROR; sql = sqlite3_mprintf("PRAGMA index_info(%Q)", rowp[i * ncols + namec]); if (sql) { dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowpp, &nnrows, &nncols, NULL); sqlite3_free(sql); } if (ret == SQLITE_OK) { size += nnrows; sqlite3_free_table(rowpp); } } } nodata: if (addipk) { size++; } if (size == 0) { sqlite3_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 = 0; int nrows2, ncols2; sql = sqlite3_mprintf("PRAGMA table_info(%Q)", tname); if (sql) { dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowpp, &nrows2, &ncols2, NULL); sqlite3_free(sql); } if (ret == SQLITE_OK) { int colid, typec, roffs, namecc, uniquecc; 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 > nrows2) { 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("sqlite_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: sqlite3_free_table(rowpp); } for (i = 1; i <= nrows; i++) { int nnrows, nncols; char **rowpp = 0; if (*rowp[i * ncols + uniquec] != '0' || itype == SQL_INDEX_ALL) { int k; ret = SQLITE_ERROR; sql = sqlite3_mprintf("PRAGMA index_info(%Q)", rowp[i * ncols + namec]); if (sql) { dbtraceapi(d, "sqlite3_get_table", sql); ret = sqlite3_get_table(d->sqlite, sql, &rowpp, &nnrows, &nncols, NULL); sqlite3_free(sql); } 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; sqlite3_free_table(rowpp); } } sqlite3_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))->cur_s3stmt == s && s->s3stmt) { s->rowp = 0; for (; i < s->rowset_size; i++) { if (s->max_rows && s->s3stmt_rownum + 1 >= s->max_rows) { ret = (i == 0) ? SQL_NO_DATA : SQL_SUCCESS; break; } ret = s3stmt_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 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 #ifdef SQL_DESC_BASE_TABLE_NAME case SQL_DESC_BASE_TABLE_NAME: #endif case SQL_DESC_TABLE_NAME: 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 #ifdef SQL_BIGINT case SQL_BIGINT: v = SQL_C_SBIGINT; 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; } /** * Internal function to setup column name/type information * @param s statement poiner * @param s3stmt SQLite3 statement pointer * @param ncolsp pointer to preinitialized number of colums * @result ODBC error code */ static SQLRETURN setupdyncols(STMT *s, sqlite3_stmt *s3stmt, int *ncolsp) { int ncols = *ncolsp, guessed_types = 0; SQLRETURN ret = SQL_SUCCESS; if (ncols > 0) { int i; PTRDIFF_T size; char *p; COL *dyncols; DBC *d = (DBC *) s->dbc; const char *colname, *typename; #if defined(HAVE_SQLITE3COLUMNTABLENAME) && (HAVE_SQLITE3COLUMNTABLENAME) char *tblname; #endif for (i = size = 0; i < ncols; i++) { colname = sqlite3_column_name(s3stmt, i); size += 3 + 3 * strlen(colname); } #if defined(HAVE_SQLITE3COLUMNTABLENAME) && (HAVE_SQLITE3COLUMNTABLENAME) tblname = (char *) size; for (i = 0; i < ncols; i++) { p = (char *) sqlite3_column_table_name(s3stmt, i); size += 2 + (p ? strlen(p) : 0); } #endif dyncols = xmalloc(ncols * sizeof (COL) + size); if (!dyncols) { freedyncols(s); *ncolsp = 0; ret = SQL_ERROR; } else { p = (char *) (dyncols + ncols); #if defined(HAVE_SQLITE3COLUMNTABLENAME) && (HAVE_SQLITE3COLUMNTABLENAME) tblname = p + (PTRDIFF_T) tblname; #endif for (i = 0; i < ncols; i++) { char *q; colname = sqlite3_column_name(s3stmt, i); if (d->trace) { fprintf(d->trace, "-- column %d name: '%s'\n", i + 1, colname); fflush(d->trace); } #if defined(HAVE_SQLITE3COLUMNTABLENAME) && (HAVE_SQLITE3COLUMNTABLENAME) q = (char *) sqlite3_column_table_name(s3stmt, i); strcpy(tblname, q ? q : ""); if (d->trace) { fprintf(d->trace, "-- table %d name: '%s'\n", i + 1, tblname); fflush(d->trace); } dyncols[i].table = tblname; tblname += strlen(tblname) + 1; #endif typename = s3stmt_coltype(s3stmt, i, d, &guessed_types); dyncols[i].db = ((DBC *) (s->dbc))->dbname; strcpy(p, colname); dyncols[i].label = p; p += strlen(p) + 1; q = strchr(colname, '.'); if (q) { char *q2 = strchr(q + 1, '.'); /* SQLite 3.3.4 produces view.table.column sometimes */ if (q2) { q = q2; } } if (q) { #if !defined(HAVE_SQLITE3COLUMNTABLENAME) || !(HAVE_SQLITE3COLUMNTABLENAME) dyncols[i].table = p; #endif strncpy(p, colname, q - colname); p[q - colname] = '\0'; p += strlen(p) + 1; strcpy(p, q + 1); dyncols[i].column = p; p += strlen(p) + 1; } else { #if !defined(HAVE_SQLITE3COLUMNTABLENAME) || !(HAVE_SQLITE3COLUMNTABLENAME) dyncols[i].table = ""; #endif strcpy(p, colname); 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; #if defined(HAVE_SQLITE3TABLECOLUMNMETADATA) && HAVE_SQLITE3TABLECOLUMNMETADATA s3stmt_addmeta(s3stmt, i, d, &dyncols[i]); #else dyncols[i].autoinc = SQL_FALSE; dyncols[i].notnull = SQL_NULLABLE; #endif dyncols[i].typename = xstrdup(typename); } freedyncols(s); s->dyncols = s->cols = dyncols; s->dcols = ncols; fixupdyncols(s, d); s->guessed_types = guessed_types; } } return ret; } /** * 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; } s3stmt_end(s); s3stmt_drop(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); if (!s->query) { if (errp) { setstat(s, -1, errp, (*s->ov3) ? "HY000" : "S1000"); return SQL_ERROR; } return nomem(s); } errp = NULL; freeresult(s, -1); if (s->isselect > 0) { int ret, ncols, nretry = 0; const char *rest; sqlite3_stmt *s3stmt = NULL; #if defined(HAVE_SQLITE3PREPAREV2) && HAVE_SQLITE3PREPAREV2 dbtraceapi(d, "sqlite3_prepare_v2", (char *) s->query); #else dbtraceapi(d, "sqlite3_prepare", (char *) s->query); #endif do { s3stmt = NULL; #if defined(HAVE_SQLITE3PREPAREV2) && HAVE_SQLITE3PREPAREV2 ret = sqlite3_prepare_v2(d->sqlite, (char *) s->query, -1, &s3stmt, &rest); #else ret = sqlite3_prepare(d->sqlite, (char *) s->query, -1, &s3stmt, &rest); #endif if (ret != SQLITE_OK) { if (s3stmt) { sqlite3_finalize(s3stmt); s3stmt = NULL; } } } while (ret == SQLITE_SCHEMA && (++nretry) < 2); dbtracerc(d, ret, NULL); if (ret != SQLITE_OK) { if (s3stmt) { dbtraceapi(d, "sqlite3_finalize", 0); sqlite3_finalize(s3stmt); } setstat(s, ret, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", sqlite3_errmsg(d->sqlite), ret); return SQL_ERROR; } if (sqlite3_bind_parameter_count(s3stmt) != s->nparams) { dbtraceapi(d, "sqlite3_finalize", 0); sqlite3_finalize(s3stmt); setstat(s, SQLITE_ERROR, "parameter marker count incorrect", (*s->ov3) ? "HY000" : "S1000"); return SQL_ERROR; } ncols = sqlite3_column_count(s3stmt); s->guessed_types = 0; setupdyncols(s, s3stmt, &ncols); s->ncols = ncols; s->s3stmt = s3stmt; } 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; int rc, i, ncols = 0, busy_count; 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; } busy_count = 0; again: s3stmt_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) { for (i = 0; i < s->nparams; i++) { ret = setupparam(s, (char *) s->query, i); if (ret != SQL_SUCCESS) { goto cleanup; } } } freeresult(s, 0); if (s->isselect > 0 && !d->intrans && s->curtype == SQL_CURSOR_FORWARD_ONLY && d->step_enable && s->nparams == 0 && d->cur_s3stmt == NULL) { s->nrows = -1; ret = s3stmt_start(s); if (ret == SQL_SUCCESS) { goto done2; } } rc = drvgettable(s, s->s3stmt ? NULL : (char *) s->query, &s->rows, &s->nrows, &ncols, &errp, s->nparams, s->bindparms); dbtracerc(d, rc, errp); if (rc == SQLITE_BUSY) { if (busy_handler((void *) d, ++busy_count)) { if (errp) { sqlite3_free(errp); errp = NULL; } if (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->paramset_count = 0; s->paramset_nrows = 0; } s->nrows = 0; goto again; } } if (rc != SQLITE_OK) { setstat(s, rc, "%s (%d)", (*s->ov3) ? "HY000" : "S1000", errp ? errp : "unknown error", rc); if (errp) { sqlite3_free(errp); errp = NULL; } ret = SQL_ERROR; goto cleanup; } if (errp) { sqlite3_free(errp); errp = NULL; } s->rowfree = freerows; if (!s->isselect) { /* * INSERT/UPDATE/DELETE results are immediately released. */ freeresult(s, -1); s->nrows = sqlite3_changes(d->sqlite); goto done; } if (s->ncols != ncols) { /* * Weird result. */ setstat(s, -1, "broken result set %d/%d", (*s->ov3) ? "HY000" : "S1000", s->ncols, ncols); ret = SQL_ERROR; goto cleanup; } 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) #ifndef WITHOUT_DRIVERMGR /* * Windows configuration dialog stuff. */ #include #include #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_STEPAPI 5 #define KEY_SYNCP 6 #define KEY_NOTXN 7 #define KEY_SHORTNAM 8 #define KEY_LONGNAM 9 #define KEY_NOCREAT 10 #define KEY_NOWCHAR 11 #define KEY_LOADEXT 12 #define KEY_JMODE 13 #define KEY_FKSUPPORT 14 #define NUMOFKEYS 15 typedef struct { BOOL supplied; char attr[MAXPATHLEN*4]; } ATTR; typedef struct { SQLHWND parent; 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 }, { "StepAPI", KEY_STEPAPI }, { "SyncPragma", KEY_SYNCP }, { "NoTXN", KEY_NOTXN }, { "ShortNames", KEY_SHORTNAM }, { "LongNames", KEY_LONGNAM }, { "NoCreat", KEY_NOCREAT }, { "NoWCHAR", KEY_NOWCHAR }, { "LoadExt", KEY_LOADEXT }, { "JournalMode", KEY_JMODE }, { "FKSupport", KEY_FKSUPPORT }, { 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) { 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); } 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_STEPAPI].supplied) { SQLWritePrivateProfileString(dsn, "StepAPI", setupdlg->attr[KEY_STEPAPI].attr, ODBC_INI); } if (parent || setupdlg->attr[KEY_SYNCP].supplied) { SQLWritePrivateProfileString(dsn, "SyncPragma", setupdlg->attr[KEY_SYNCP].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_SHORTNAM].supplied) { SQLWritePrivateProfileString(dsn, "ShortNames", setupdlg->attr[KEY_SHORTNAM].attr, ODBC_INI); } if (parent || setupdlg->attr[KEY_LONGNAM].supplied) { SQLWritePrivateProfileString(dsn, "LongNames", setupdlg->attr[KEY_LONGNAM].attr, ODBC_INI); } if (parent || setupdlg->attr[KEY_NOCREAT].supplied) { SQLWritePrivateProfileString(dsn, "NoCreat", setupdlg->attr[KEY_NOCREAT].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_FKSUPPORT].supplied) { SQLWritePrivateProfileString(dsn, "FKSupport", setupdlg->attr[KEY_FKSUPPORT].attr, ODBC_INI); } if (parent || setupdlg->attr[KEY_LOADEXT].supplied) { SQLWritePrivateProfileString(dsn, "LoadExt", setupdlg->attr[KEY_LOADEXT].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_STEPAPI].supplied) { SQLGetPrivateProfileString(dsn, "StepAPI", "0", setupdlg->attr[KEY_STEPAPI].attr, sizeof (setupdlg->attr[KEY_STEPAPI].attr), ODBC_INI); } if (!setupdlg->attr[KEY_SYNCP].supplied) { SQLGetPrivateProfileString(dsn, "SyncPragma", "NORMAL", setupdlg->attr[KEY_SYNCP].attr, sizeof (setupdlg->attr[KEY_SYNCP].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_SHORTNAM].supplied) { SQLGetPrivateProfileString(dsn, "ShortNames", "", setupdlg->attr[KEY_SHORTNAM].attr, sizeof (setupdlg->attr[KEY_SHORTNAM].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); } if (!setupdlg->attr[KEY_NOCREAT].supplied) { SQLGetPrivateProfileString(dsn, "NoCreat", "", setupdlg->attr[KEY_NOCREAT].attr, sizeof (setupdlg->attr[KEY_NOCREAT].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_FKSUPPORT].supplied) { SQLGetPrivateProfileString(dsn, "FKSupport", "", setupdlg->attr[KEY_FKSUPPORT].attr, sizeof (setupdlg->attr[KEY_FKSUPPORT].attr), ODBC_INI); } if (!setupdlg->attr[KEY_LOADEXT].supplied) { SQLGetPrivateProfileString(dsn, "LoadExt", "", setupdlg->attr[KEY_LOADEXT].attr, sizeof (setupdlg->attr[KEY_LOADEXT].attr), ODBC_INI); } if (!setupdlg->attr[KEY_JMODE].supplied) { SQLGetPrivateProfileString(dsn, "JournalMode", "", setupdlg->attr[KEY_JMODE].attr, sizeof (setupdlg->attr[KEY_JMODE].attr), ODBC_INI); } } /** * Open file dialog for selection of SQLite database file. * @param hdlg handle of originating dialog window */ static void GetDBFile(HWND hdlg) { #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; } } /** * 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; WORD index; 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); SetDlgItemText(hdlg, IDC_LOADEXT, setupdlg->attr[KEY_LOADEXT].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); SendDlgItemMessage(hdlg, IDC_LOADEXT, EM_LIMITTEXT, (WPARAM) (MAXPATHLEN*4 - 1), (LPARAM) 0); 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_SHORTNAM, getbool(setupdlg->attr[KEY_SHORTNAM].attr) ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hdlg, IDC_LONGNAM, getbool(setupdlg->attr[KEY_LONGNAM].attr) ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hdlg, IDC_NOCREAT, getbool(setupdlg->attr[KEY_NOCREAT].attr) ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hdlg, IDC_NOWCHAR, getbool(setupdlg->attr[KEY_NOWCHAR].attr) ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hdlg, IDC_FKSUPPORT, getbool(setupdlg->attr[KEY_FKSUPPORT].attr) ? BST_CHECKED : BST_UNCHECKED); SendDlgItemMessage(hdlg, IDC_SYNCP, CB_LIMITTEXT, (WPARAM) 10, (LPARAM) 0); SendDlgItemMessage(hdlg, IDC_SYNCP, CB_ADDSTRING, 0, (LPARAM) "NORMAL"); SendDlgItemMessage(hdlg, IDC_SYNCP, CB_ADDSTRING, 0, (LPARAM) "OFF"); SendDlgItemMessage(hdlg, IDC_SYNCP, CB_ADDSTRING, 0, (LPARAM) "FULL"); SendDlgItemMessage(hdlg, IDC_SYNCP, CB_SELECTSTRING, (WPARAM) -1, (LPARAM) setupdlg->attr[KEY_SYNCP].attr); 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)); GetDlgItemText(hdlg, IDC_LOADEXT, setupdlg->attr[KEY_LOADEXT].attr, sizeof (setupdlg->attr[KEY_LOADEXT].attr)); index = SendDlgItemMessage(hdlg, IDC_SYNCP, CB_GETCURSEL, (WPARAM) 0, (LPARAM) 0); if (index != (WORD) CB_ERR) { SendDlgItemMessage(hdlg, IDC_SYNCP, CB_GETLBTEXT, index, (LPARAM) setupdlg->attr[KEY_SYNCP].attr); } 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_SHORTNAM].attr, (IsDlgButtonChecked(hdlg, IDC_SHORTNAM) == BST_CHECKED) ? "1" : "0"); strcpy(setupdlg->attr[KEY_LONGNAM].attr, (IsDlgButtonChecked(hdlg, IDC_LONGNAM) == BST_CHECKED) ? "1" : "0"); strcpy(setupdlg->attr[KEY_NOCREAT].attr, (IsDlgButtonChecked(hdlg, IDC_NOCREAT) == BST_CHECKED) ? "1" : "0"); strcpy(setupdlg->attr[KEY_NOWCHAR].attr, (IsDlgButtonChecked(hdlg, IDC_NOWCHAR) == BST_CHECKED) ? "1" : "0"); strcpy(setupdlg->attr[KEY_FKSUPPORT].attr, (IsDlgButtonChecked(hdlg, IDC_FKSUPPORT) == BST_CHECKED) ? "1" : "0"); SetDSNAttributes(hdlg, setupdlg); /* FALL THROUGH */ case IDCANCEL: EndDialog(hdlg, wparam); return TRUE; } break; } return FALSE; } /** * 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 { setupdlg->parent = hwnd; setupdlg->driver = driver; setupdlg->newDSN = request == ODBC_ADD_DSN; setupdlg->defDSN = strcasecmp(setupdlg->attr[KEY_DSN].attr, "Default") == 0; if (hwnd) { success = DialogBoxParam(hModule, MAKEINTRESOURCE(CONFIGDSN), hwnd, (DLGPROC) ConfigDlgProc, (LPARAM) setupdlg) == IDOK; } else if (setupdlg->attr[KEY_DSN].supplied) { success = SetDSNAttributes(hwnd, setupdlg); } else { success = FALSE; } } xfree(setupdlg); return success; } /** * 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; WORD index; 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); SetDlgItemText(hdlg, IDC_LOADEXT, setupdlg->attr[KEY_LOADEXT].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); SendDlgItemMessage(hdlg, IDC_LOADEXT, EM_LIMITTEXT, (WPARAM) (MAXPATHLEN*4 - 1), (LPARAM) 0); 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_SHORTNAM, getbool(setupdlg->attr[KEY_SHORTNAM].attr) ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hdlg, IDC_LONGNAM, getbool(setupdlg->attr[KEY_LONGNAM].attr) ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hdlg, IDC_NOCREAT, getbool(setupdlg->attr[KEY_NOCREAT].attr) ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hdlg, IDC_NOWCHAR, getbool(setupdlg->attr[KEY_NOWCHAR].attr) ? BST_CHECKED : BST_UNCHECKED); CheckDlgButton(hdlg, IDC_FKSUPPORT, getbool(setupdlg->attr[KEY_FKSUPPORT].attr) ? BST_CHECKED : BST_UNCHECKED); SendDlgItemMessage(hdlg, IDC_SYNCP, CB_LIMITTEXT, (WPARAM) 10, (LPARAM) 0); SendDlgItemMessage(hdlg, IDC_SYNCP, CB_ADDSTRING, 0, (LPARAM) "NORMAL"); SendDlgItemMessage(hdlg, IDC_SYNCP, CB_ADDSTRING, 0, (LPARAM) "OFF"); SendDlgItemMessage(hdlg, IDC_SYNCP, CB_ADDSTRING, 0, (LPARAM) "FULL"); SendDlgItemMessage(hdlg, IDC_SYNCP, CB_SELECTSTRING, (WORD) -1, (LPARAM) setupdlg->attr[KEY_SYNCP].attr); 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)); GetDlgItemText(hdlg, IDC_LOADEXT, setupdlg->attr[KEY_LOADEXT].attr, sizeof (setupdlg->attr[KEY_LOADEXT].attr)); index = SendDlgItemMessage(hdlg, IDC_SYNCP, CB_GETCURSEL, (WPARAM) 0, (LPARAM) 0); if (index != (WORD) CB_ERR) { SendDlgItemMessage(hdlg, IDC_SYNCP, CB_GETLBTEXT, index, (LPARAM) setupdlg->attr[KEY_SYNCP].attr); } 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_SHORTNAM].attr, (IsDlgButtonChecked(hdlg, IDC_SHORTNAM) == BST_CHECKED) ? "1" : "0"); strcpy(setupdlg->attr[KEY_LONGNAM].attr, (IsDlgButtonChecked(hdlg, IDC_LONGNAM) == BST_CHECKED) ? "1" : "0"); strcpy(setupdlg->attr[KEY_NOCREAT].attr, (IsDlgButtonChecked(hdlg, IDC_NOCREAT) == BST_CHECKED) ? "1" : "0"); strcpy(setupdlg->attr[KEY_NOWCHAR].attr, (IsDlgButtonChecked(hdlg, IDC_NOWCHAR) == BST_CHECKED) ? "1" : "0"); strcpy(setupdlg->attr[KEY_FKSUPPORT].attr, (IsDlgButtonChecked(hdlg, IDC_FKSUPPORT) == BST_CHECKED) ? "1" : "0"); /* FALL THROUGH */ case IDCANCEL: EndDialog(hdlg, GET_WM_COMMAND_ID(wparam, lparam) == IDOK); return TRUE; } } return FALSE; } /** * 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; char *dsn = NULL, *driver = NULL, *dbname = NULL; 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'; dlgret = DialogBoxParam(hModule, MAKEINTRESOURCE(DRIVERCONNECT), hwnd, (DLGPROC) DriverConnectProc, (LPARAM) setupdlg); if (!dlgret || dlgret == -1) { xfree(setupdlg); return SQL_NO_DATA; } } dsn = setupdlg->attr[KEY_DSN].attr; driver = setupdlg->attr[KEY_DRIVER].attr; dbname = setupdlg->attr[KEY_DBNAME].attr; if (connOut || connOutLen) { char buf[2048]; int len, count; char dsn_0 = dsn ? dsn[0] : '\0'; char drv_0 = driver ? driver[0] : '\0'; buf[0] = '\0'; count = snprintf(buf, sizeof (buf), "%s%s%s%s%s%sDatabase=%s;StepAPI=%s;" "SyncPragma=%s;NoTXN=%s;Timeout=%s;" "ShortNames=%s;LongNames=%s;" "NoCreat=%s;NoWCHAR=%s;" "FKSupport=%s;JournalMode=%s;LoadExt=%s;", dsn_0 ? "DSN=" : "", dsn_0 ? dsn : "", dsn_0 ? ";" : "", drv_0 ? "Driver=" : "", drv_0 ? driver : "", drv_0 ? ";" : "", dbname ? dbname : "", setupdlg->attr[KEY_STEPAPI].attr, setupdlg->attr[KEY_SYNCP].attr, setupdlg->attr[KEY_NOTXN].attr, setupdlg->attr[KEY_BUSY].attr, setupdlg->attr[KEY_SHORTNAM].attr, setupdlg->attr[KEY_LONGNAM].attr, setupdlg->attr[KEY_NOCREAT].attr, setupdlg->attr[KEY_NOWCHAR].attr, setupdlg->attr[KEY_FKSUPPORT].attr, setupdlg->attr[KEY_JMODE].attr, setupdlg->attr[KEY_LOADEXT].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 (dsn[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"); } } d->nowchar = getbool(setupdlg->attr[KEY_NOWCHAR].attr); d->shortnames = getbool(setupdlg->attr[KEY_SHORTNAM].attr); d->longnames = getbool(setupdlg->attr[KEY_LONGNAM].attr); d->nocreat = getbool(setupdlg->attr[KEY_NOCREAT].attr); d->fksupport = getbool(setupdlg->attr[KEY_FKSUPPORT].attr); ret = dbopen(d, dbname ? dbname : "", 0, dsn ? dsn : "", setupdlg->attr[KEY_STEPAPI].attr, setupdlg->attr[KEY_SYNCP].attr, setupdlg->attr[KEY_NOTXN].attr, setupdlg->attr[KEY_JMODE].attr, setupdlg->attr[KEY_BUSY].attr); if (ret != SQL_SUCCESS) { if (maybeprompt && !prompt) { prompt = TRUE; goto retry; } } if (ret == SQL_SUCCESS) { dbloadext(d, setupdlg->attr[KEY_LOADEXT].attr); } xfree(setupdlg); return ret; } #endif /* WITHOUT_DRIVERMGR */ #endif /* _WIN32 || _WIN64 */ #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) { #if defined(_WIN32) || defined(_WIN64) ci = uc_to_wmb(connIn, connInLen); #else ci = uc_to_utf(connIn, connInLen); #endif 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) { #if defined(_WIN32) || defined(_WIN64) co = wmb_to_uc((char *) connOut, len); #else co = uc_from_utf((SQLCHAR *) connOut, len); #endif 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 } #if defined(ENABLE_NVFS) && ENABLE_NVFS nvfs_init(); #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) { static char *drivername = "SQLite3 ODBC Driver"; static char *dsname = "SQLite3 Datasource"; 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 sqlite3_main(int, char **); static const char *name = "SQLite3 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); #if defined(ENABLE_NVFS) && ENABLE_NVFS nvfs_init(); #endif sqlite3_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 int ODBCINSTGetProperties(HODBCINSTPROPERTY prop) { static const char *instYN[] = { "No", "Yes", NULL }; static const char *syncPragma[] = { "NORMAL", "OFF", "FULL", 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); 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, "ShortNames", INI_MAX_PROPERTY_NAME); strncpy(prop->szValue, "No", 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, "LongNames", INI_MAX_PROPERTY_NAME); strncpy(prop->szValue, "No", INI_MAX_PROPERTY_VALUE); prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); memset(prop, 0, sizeof (ODBCINSTPROPERTY)); prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX; prop->aPromptData = malloc(sizeof (instYN)); memcpy(prop->aPromptData, instYN, sizeof (instYN)); strncpy(prop->szName, "NoCreat", INI_MAX_PROPERTY_NAME); strncpy(prop->szValue, "No", INI_MAX_PROPERTY_VALUE); #ifdef WINTERFACE prop->pNext = (HODBCINSTPROPERTY) malloc(sizeof (ODBCINSTPROPERTY)); 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)); memset(prop, 0, sizeof (ODBCINSTPROPERTY)); prop->nPromptType = ODBCINST_PROMPTTYPE_COMBOBOX; prop->aPromptData = malloc(sizeof (instYN)); memcpy(prop->aPromptData, instYN, sizeof (instYN)); strncpy(prop->szName, "FKSupport", INI_MAX_PROPERTY_NAME); strncpy(prop->szValue, "No", 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 (syncPragma)); memcpy(prop->aPromptData, syncPragma, sizeof (syncPragma)); strncpy(prop->szName, "SyncPragma", INI_MAX_PROPERTY_NAME); strncpy(prop->szValue, "NORMAL", 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, "LoadExt", INI_MAX_PROPERTY_NAME); strncpy(prop->szValue, "", INI_MAX_PROPERTY_VALUE); return 1; } #endif /* HAVE_ODBCINSTEXT_H */