diff options
Diffstat (limited to 'src/interfaces/odbc/convert.c')
-rw-r--r-- | src/interfaces/odbc/convert.c | 3578 |
1 files changed, 0 insertions, 3578 deletions
diff --git a/src/interfaces/odbc/convert.c b/src/interfaces/odbc/convert.c deleted file mode 100644 index 2b586192320..00000000000 --- a/src/interfaces/odbc/convert.c +++ /dev/null @@ -1,3578 +0,0 @@ -/*------- - * Module: convert.c - * - * Description: This module contains routines related to - * converting parameters and columns into requested data types. - * Parameters are converted from their SQL_C data types into - * the appropriate postgres type. Columns are converted from - * their postgres type (SQL type) into the appropriate SQL_C - * data type. - * - * Classes: n/a - * - * API functions: none - * - * Comments: See "notice.txt" for copyright and license information. - *------- - */ -/* Multibyte support Eiji Tokuya 2001-03-15 */ - -#include "convert.h" - -#include <stdio.h> -#include <string.h> -#include <ctype.h> - -#ifdef MULTIBYTE -#include "multibyte.h" -#endif - -#include <time.h> -#ifdef HAVE_LOCALE_H -#include <locale.h> -#endif -#include <math.h> -#include <stdlib.h> -#include "statement.h" -#include "qresult.h" -#include "bind.h" -#include "pgtypes.h" -#include "lobj.h" -#include "connection.h" -#include "pgapifunc.h" - -#ifdef __CYGWIN__ -#define TIMEZONE_GLOBAL _timezone -#elif defined(WIN32) || defined(HAVE_INT_TIMEZONE) -#define TIMEZONE_GLOBAL timezone -#endif - -/* - * How to map ODBC scalar functions {fn func(args)} to Postgres. - * This is just a simple substitution. List augmented from: - * http://www.merant.com/datadirect/download/docs/odbc16/Odbcref/rappc.htm - * - thomas 2000-04-03 - */ -char *mapFuncs[][2] = { -/* { "ASCII", "ascii" }, built_in */ - {"CHAR", "chr($*)" }, - {"CONCAT", "textcat($*)" }, -/* { "DIFFERENCE", "difference" }, how to ? */ - {"INSERT", "substring($1 from 1 for $2 - 1) || $4 || substring($1 from $2 + $3)" }, - {"LCASE", "lower($*)" }, - {"LEFT", "ltrunc($*)" }, - {"%2LOCATE", "strpos($2, $1)" }, /* 2 parameters */ - {"%3LOCATE", "strpos(substring($2 from $3), $1) + $3 - 1" }, /* 3 parameters */ - {"LENGTH", "char_length($*)"}, -/* { "LTRIM", "ltrim" }, built_in */ - {"RIGHT", "rtrunc($*)" }, - {"SPACE", "repeat('' '', $1)" }, -/* { "REPEAT", "repeat" }, built_in */ -/* { "REPLACE", "replace" }, ??? */ -/* { "RTRIM", "rtrim" }, built_in */ -/* { "SOUNDEX", "soundex" }, how to ? */ - {"SUBSTRING", "substr($*)" }, - {"UCASE", "upper($*)" }, - -/* { "ABS", "abs" }, built_in */ -/* { "ACOS", "acos" }, built_in */ -/* { "ASIN", "asin" }, built_in */ -/* { "ATAN", "atan" }, built_in */ -/* { "ATAN2", "atan2" }, bui;t_in */ - {"CEILING", "ceil($*)" }, -/* { "COS", "cos" }, built_in */ -/* { "COT", "cot" }, built_in */ -/* { "DEGREES", "degrees" }, built_in */ -/* { "EXP", "exp" }, built_in */ -/* { "FLOOR", "floor" }, built_in */ - {"LOG", "ln($*)" }, - {"LOG10", "log($*)" }, -/* { "MOD", "mod" }, built_in */ -/* { "PI", "pi" }, built_in */ - {"POWER", "pow($*)" }, -/* { "RADIANS", "radians" }, built_in */ - {"%0RAND", "random()" }, /* 0 parameters */ - {"%1RAND", "(setseed($1) * .0 + random())" }, /* 1 parameters */ -/* { "ROUND", "round" }, built_in */ -/* { "SIGN", "sign" }, built_in */ -/* { "SIN", "sin" }, built_in */ -/* { "SQRT", "sqrt" }, built_in */ -/* { "TAN", "tan" }, built_in */ - {"TRUNCATE", "trunc($*)" }, - - {"CURRENT_DATE", "current_date" }, - {"CURRENT_TIME", "current_time" }, - {"CURRENT_TIMESTAMP", "current_timestamp" }, - {"LOCALTIME", "localtime" }, - {"LOCALTIMESTAMP", "localtimestamp" }, - {"CURRENT_USER", "cast(current_user as text)" }, - {"SESSION_USER", "cast(session_user as text)" }, - {"CURDATE", "current_date" }, - {"CURTIME", "current_time" }, - {"DAYNAME", "to_char($1, 'Day')" }, - {"DAYOFMONTH", "cast(extract(day from $1) as integer)" }, - {"DAYOFWEEK", "(cast(extract(dow from $1) as integer) + 1)" }, - {"DAYOFYEAR", "cast(extract(doy from $1) as integer)" }, - {"HOUR", "cast(extract(hour from $1) as integer)" }, - {"MINUTE", "cast(extract(minute from $1) as integer)" }, - {"MONTH", "cast(extract(month from $1) as integer)" }, - {"MONTHNAME", " to_char($1, 'Month')" }, -/* { "NOW", "now" }, built_in */ - {"QUARTER", "cast(extract(quarter from $1) as integer)" }, - {"SECOND", "cast(extract(second from $1) as integer)" }, - {"WEEK", "cast(extract(week from $1) as integer)" }, - {"YEAR", "cast(extract(year from $1) as integer)" }, - -/* { "DATABASE", "database" }, */ - {"IFNULL", "coalesce($*)" }, - {"USER", "cast(current_user as text)" }, - {0, 0} -}; - -static const char *mapFunction(const char *func, int param_count); -static unsigned int conv_from_octal(const unsigned char *s); -static unsigned int conv_from_hex(const unsigned char *s); -static char *conv_to_octal(unsigned char val); - -/*--------- - * A Guide for date/time/timestamp conversions - * - * field_type fCType Output - * ---------- ------ ---------- - * PG_TYPE_DATE SQL_C_DEFAULT SQL_C_DATE - * PG_TYPE_DATE SQL_C_DATE SQL_C_DATE - * PG_TYPE_DATE SQL_C_TIMESTAMP SQL_C_TIMESTAMP (time = 0 (midnight)) - * PG_TYPE_TIME SQL_C_DEFAULT SQL_C_TIME - * PG_TYPE_TIME SQL_C_TIME SQL_C_TIME - * PG_TYPE_TIME SQL_C_TIMESTAMP SQL_C_TIMESTAMP (date = current date) - * PG_TYPE_ABSTIME SQL_C_DEFAULT SQL_C_TIMESTAMP - * PG_TYPE_ABSTIME SQL_C_DATE SQL_C_DATE (time is truncated) - * PG_TYPE_ABSTIME SQL_C_TIME SQL_C_TIME (date is truncated) - * PG_TYPE_ABSTIME SQL_C_TIMESTAMP SQL_C_TIMESTAMP - *--------- - */ - - - -/* - * TIMESTAMP <-----> SIMPLE_TIME - * precision support since 7.2. - * time zone support is unavailable(the stuff is unreliable) - */ -static BOOL -timestamp2stime(const char *str, SIMPLE_TIME *st, BOOL *bZone, int *zone) -{ - char rest[64], - *ptr; - int scnt, - i; -#if defined(WIN32) || defined(HAVE_INT_TIMEZONE) - long timediff; -#endif - BOOL withZone = *bZone; - - *bZone = FALSE; - *zone = 0; - st->fr = 0; - st->infinity = 0; - if ((scnt = sscanf(str, "%4d-%2d-%2d %2d:%2d:%2d%s", &st->y, &st->m, &st->d, &st->hh, &st->mm, &st->ss, rest)) < 6) - return FALSE; - else if (scnt == 6) - return TRUE; - switch (rest[0]) - { - case '+': - *bZone = TRUE; - *zone = atoi(&rest[1]); - break; - case '-': - *bZone = TRUE; - *zone = -atoi(&rest[1]); - break; - case '.': - if ((ptr = strchr(rest, '+')) != NULL) - { - *bZone = TRUE; - *zone = atoi(&ptr[1]); - *ptr = '\0'; - } - else if ((ptr = strchr(rest, '-')) != NULL) - { - *bZone = TRUE; - *zone = -atoi(&ptr[1]); - *ptr = '\0'; - } - for (i = 1; i < 10; i++) - { - if (!isdigit((unsigned char) rest[i])) - break; - } - for (; i < 10; i++) - rest[i] = '0'; - rest[i] = '\0'; - st->fr = atoi(&rest[1]); - break; - default: - return TRUE; - } - if (!withZone || !*bZone || st->y < 1970) - return TRUE; -#if defined(WIN32) || defined(HAVE_INT_TIMEZONE) - if (!tzname[0] || !tzname[0][0]) - { - *bZone = FALSE; - return TRUE; - } - timediff = TIMEZONE_GLOBAL + (*zone) * 3600; - if (!daylight && timediff == 0) /* the same timezone */ - return TRUE; - else - { - struct tm tm, - *tm2; - time_t time0; - - *bZone = FALSE; - tm.tm_year = st->y - 1900; - tm.tm_mon = st->m - 1; - tm.tm_mday = st->d; - tm.tm_hour = st->hh; - tm.tm_min = st->mm; - tm.tm_sec = st->ss; - tm.tm_isdst = -1; - time0 = mktime(&tm); - if (time0 < 0) - return TRUE; - if (tm.tm_isdst > 0) - timediff -= 3600; - if (timediff == 0) /* the same time zone */ - return TRUE; - time0 -= timediff; - if (time0 >= 0 && (tm2 = localtime(&time0)) != NULL) - { - st->y = tm2->tm_year + 1900; - st->m = tm2->tm_mon + 1; - st->d = tm2->tm_mday; - st->hh = tm2->tm_hour; - st->mm = tm2->tm_min; - st->ss = tm2->tm_sec; - *bZone = TRUE; - } - } -#endif /* WIN32 */ - return TRUE; -} - -static BOOL -stime2timestamp(const SIMPLE_TIME *st, char *str, BOOL bZone, BOOL precision) -{ - char precstr[16], - zonestr[16]; - int i; - - precstr[0] = '\0'; - if (st->infinity > 0) - { - strcpy(str, "Infinity"); - return TRUE; - } - else if (st->infinity < 0) - { - strcpy(str, "-Infinity"); - return TRUE; - } - if (precision && st->fr) - { - sprintf(precstr, ".%09d", st->fr); - for (i = 9; i > 0; i--) - { - if (precstr[i] != '0') - break; - precstr[i] = '\0'; - } - } - zonestr[0] = '\0'; -#if defined(WIN32) || defined(HAVE_INT_TIMEZONE) - if (bZone && tzname[0] && tzname[0][0] && st->y >= 1970) - { - long zoneint; - struct tm tm; - time_t time0; - - zoneint = TIMEZONE_GLOBAL; - if (daylight && st->y >= 1900) - { - tm.tm_year = st->y - 1900; - tm.tm_mon = st->m - 1; - tm.tm_mday = st->d; - tm.tm_hour = st->hh; - tm.tm_min = st->mm; - tm.tm_sec = st->ss; - tm.tm_isdst = -1; - time0 = mktime(&tm); - if (time0 >= 0 && tm.tm_isdst > 0) - zoneint -= 3600; - } - if (zoneint > 0) - sprintf(zonestr, "-%02d", (int) zoneint / 3600); - else - sprintf(zonestr, "+%02d", -(int) zoneint / 3600); - } -#endif /* WIN32 */ - sprintf(str, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d%s%s", st->y, st->m, st->d, st->hh, st->mm, st->ss, precstr, zonestr); - return TRUE; -} - -/* This is called by SQLFetch() */ -int -copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col) -{ - ARDFields *opts = SC_get_ARD(stmt); - BindInfoClass *bic = &(opts->bindings[col]); - UInt4 offset = opts->row_offset_ptr ? *opts->row_offset_ptr : 0; - - return copy_and_convert_field(stmt, field_type, value, (Int2) bic->returntype, (PTR) (bic->buffer + offset), - (SDWORD) bic->buflen, (SDWORD *) (bic->used + (offset >> 2))); -} - - -/* This is called by SQLGetData() */ -int -copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType, - PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue) -{ - static char *func = "copy_and_convert_field"; - ARDFields *opts = SC_get_ARD(stmt); - Int4 len = 0, - copy_len = 0; - SIMPLE_TIME st; - time_t t = time(NULL); - struct tm *tim; - int pcbValueOffset, - rgbValueOffset; - char *rgbValueBindRow; - const char *ptr; - int bind_row = stmt->bind_row; - int bind_size = opts->bind_size; - int result = COPY_OK; -#ifdef HAVE_LOCALE_H - char saved_locale[256]; -#endif /* HAVE_LOCALE_H */ - BOOL changed, true_is_minus1 = FALSE; - const char *neut_str = value; - char midtemp[2][32]; - int mtemp_cnt = 0; - static BindInfoClass sbic; - BindInfoClass *pbic; -#ifdef UNICODE_SUPPORT - BOOL wchanged = FALSE; -#endif /* UNICODE_SUPPORT */ - - if (stmt->current_col >= 0) - { - pbic = &opts->bindings[stmt->current_col]; - if (pbic->data_left == -2) - pbic->data_left = (cbValueMax > 0) ? 0 : -1; /* This seems to be * - * needed by ADO ? */ - if (pbic->data_left == 0) - { - if (pbic->ttlbuf != NULL) - { - free(pbic->ttlbuf); - pbic->ttlbuf = NULL; - pbic->ttlbuflen = 0; - } - pbic->data_left = -2; /* needed by ADO ? */ - return COPY_NO_DATA_FOUND; - } - } - /*--------- - * rgbValueOffset is *ONLY* for character and binary data. - * pcbValueOffset is for computing any pcbValue location - *--------- - */ - - if (bind_size > 0) - pcbValueOffset = rgbValueOffset = (bind_size * bind_row); - else - { - pcbValueOffset = bind_row * sizeof(SDWORD); - rgbValueOffset = bind_row * cbValueMax; - - } - - memset(&st, 0, sizeof(SIMPLE_TIME)); - - /* Initialize current date */ - tim = localtime(&t); - st.m = tim->tm_mon + 1; - st.d = tim->tm_mday; - st.y = tim->tm_year + 1900; - - mylog("copy_and_convert: field_type = %d, fctype = %d, value = '%s', cbValueMax=%d\n", field_type, fCType, (value == NULL) ? "<NULL>" : value, cbValueMax); - - if (!value) - { - /* - * handle a null just by returning SQL_NULL_DATA in pcbValue, and - * doing nothing to the buffer. - */ - if (pcbValue) - { - *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = SQL_NULL_DATA; - return COPY_OK; - } - else - { - stmt->errornumber = STMT_RETURN_NULL_WITHOUT_INDICATOR; - stmt->errormsg = "StrLen_or_IndPtr was a null pointer and NULL data was retrieved"; - SC_log_error(func, "", stmt); - return SQL_ERROR; - } - } - - if (stmt->hdbc->DataSourceToDriver != NULL) - { - int length = strlen(value); - - stmt->hdbc->DataSourceToDriver(stmt->hdbc->translation_option, - SQL_CHAR, - value, length, - value, length, NULL, - NULL, 0, NULL); - } - - /* - * First convert any specific postgres types into more useable data. - * - * NOTE: Conversions from PG char/varchar of a date/time/timestamp value - * to SQL_C_DATE,SQL_C_TIME, SQL_C_TIMESTAMP not supported - */ - switch (field_type) - { - /* - * $$$ need to add parsing for date/time/timestamp strings in - * PG_TYPE_CHAR,VARCHAR $$$ - */ - case PG_TYPE_DATE: - sscanf(value, "%4d-%2d-%2d", &st.y, &st.m, &st.d); - break; - - case PG_TYPE_TIME: - sscanf(value, "%2d:%2d:%2d", &st.hh, &st.mm, &st.ss); - break; - - case PG_TYPE_ABSTIME: - case PG_TYPE_DATETIME: - case PG_TYPE_TIMESTAMP_NO_TMZONE: - case PG_TYPE_TIMESTAMP: - st.fr = 0; - st.infinity = 0; - if (strnicmp(value, "infinity", 8) == 0) - { - st.infinity = 1; - st.m = 12; - st.d = 31; - st.y = 9999; - st.hh = 23; - st.mm = 59; - st.ss = 59; - } - if (strnicmp(value, "-infinity", 9) == 0) - { - st.infinity = -1; - st.m = 0; - st.d = 0; - st.y = 0; - st.hh = 0; - st.mm = 0; - st.ss = 0; - } - if (strnicmp(value, "invalid", 7) != 0) - { - BOOL bZone = (field_type != PG_TYPE_TIMESTAMP_NO_TMZONE && PG_VERSION_GE(SC_get_conn(stmt), 7.2)); - int zone; - - /* - * sscanf(value, "%4d-%2d-%2d %2d:%2d:%2d", &st.y, &st.m, - * &st.d, &st.hh, &st.mm, &st.ss); - */ - bZone = FALSE; /* time zone stuff is unreliable */ - timestamp2stime(value, &st, &bZone, &zone); - } - else - { - /* - * The timestamp is invalid so set something conspicuous, - * like the epoch - */ - t = 0; - tim = localtime(&t); - st.m = tim->tm_mon + 1; - st.d = tim->tm_mday; - st.y = tim->tm_year + 1900; - st.hh = tim->tm_hour; - st.mm = tim->tm_min; - st.ss = tim->tm_sec; - } - break; - - case PG_TYPE_BOOL: - { /* change T/F to 1/0 */ - char *s; - - s = midtemp[mtemp_cnt]; - switch (((char *)value)[0]) - { - case 'f': - case 'F': - case 'n': - case 'N': - case '0': - strcpy(s, "0"); - break; - default: - if (true_is_minus1) - strcpy(s, "-1"); - else - strcpy(s, "1"); - } - neut_str = midtemp[mtemp_cnt]; - mtemp_cnt++; - } - break; - - /* This is for internal use by SQLStatistics() */ - case PG_TYPE_INT2VECTOR: - { - int nval, - i; - const char *vp; - - /* this is an array of eight integers */ - short *short_array = (short *) ((char *) rgbValue + rgbValueOffset); - - len = 32; - vp = value; - nval = 0; - mylog("index=("); - for (i = 0; i < 16; i++) - { - if (sscanf(vp, "%hd", &short_array[i]) != 1) - break; - - mylog(" %d", short_array[i]); - nval++; - - /* skip the current token */ - while ((*vp != '\0') && (!isspace((unsigned char) *vp))) - vp++; - /* and skip the space to the next token */ - while ((*vp != '\0') && (isspace((unsigned char) *vp))) - vp++; - if (*vp == '\0') - break; - } - mylog(") nval = %d\n", nval); - - for (i = nval; i < 16; i++) - short_array[i] = 0; - -#if 0 - sscanf(value, "%hd %hd %hd %hd %hd %hd %hd %hd", - &short_array[0], - &short_array[1], - &short_array[2], - &short_array[3], - &short_array[4], - &short_array[5], - &short_array[6], - &short_array[7]); -#endif - - /* There is no corresponding fCType for this. */ - if (pcbValue) - *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = len; - - return COPY_OK; /* dont go any further or the data will be - * trashed */ - } - - /* - * This is a large object OID, which is used to store - * LONGVARBINARY objects. - */ - case PG_TYPE_LO: - - return convert_lo(stmt, value, fCType, ((char *) rgbValue + rgbValueOffset), cbValueMax, (SDWORD *) ((char *) pcbValue + pcbValueOffset)); - - default: - - if (field_type == stmt->hdbc->lobj_type) /* hack until permanent - * type available */ - return convert_lo(stmt, value, fCType, ((char *) rgbValue + rgbValueOffset), cbValueMax, (SDWORD *) ((char *) pcbValue + pcbValueOffset)); - } - - /* Change default into something useable */ - if (fCType == SQL_C_DEFAULT) - { - fCType = pgtype_to_ctype(stmt, field_type); - - mylog("copy_and_convert, SQL_C_DEFAULT: fCType = %d\n", fCType); - } - - rgbValueBindRow = (char *) rgbValue + rgbValueOffset; - -#ifdef UNICODE_SUPPORT - if (fCType == SQL_C_CHAR || fCType == SQL_C_WCHAR) -#else - if (fCType == SQL_C_CHAR) -#endif /* UNICODE_SUPPORT */ - { - /* Special character formatting as required */ - - /* - * These really should return error if cbValueMax is not big - * enough. - */ - switch (field_type) - { - case PG_TYPE_DATE: - len = 10; - if (cbValueMax > len) - sprintf(rgbValueBindRow, "%.4d-%.2d-%.2d", st.y, st.m, st.d); - break; - - case PG_TYPE_TIME: - len = 8; - if (cbValueMax > len) - sprintf(rgbValueBindRow, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss); - break; - - case PG_TYPE_ABSTIME: - case PG_TYPE_DATETIME: - case PG_TYPE_TIMESTAMP_NO_TMZONE: - case PG_TYPE_TIMESTAMP: - len = 19; - if (cbValueMax > len) - sprintf(rgbValueBindRow, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d", - st.y, st.m, st.d, st.hh, st.mm, st.ss); - break; - - case PG_TYPE_BOOL: - len = strlen(neut_str); - if (cbValueMax > len) - { - strcpy(rgbValueBindRow, neut_str); - mylog("PG_TYPE_BOOL: rgbValueBindRow = '%s'\n", rgbValueBindRow); - } - break; - - /* - * Currently, data is SILENTLY TRUNCATED for BYTEA and - * character data types if there is not enough room in - * cbValueMax because the driver can't handle multiple - * calls to SQLGetData for these, yet. Most likely, the - * buffer passed in will be big enough to handle the - * maximum limit of postgres, anyway. - * - * LongVarBinary types are handled correctly above, observing - * truncation and all that stuff since there is - * essentially no limit on the large object used to store - * those. - */ - case PG_TYPE_BYTEA:/* convert binary data to hex strings - * (i.e, 255 = "FF") */ - len = convert_pgbinary_to_char(neut_str, rgbValueBindRow, cbValueMax); - - /***** THIS IS NOT PROPERLY IMPLEMENTED *****/ - break; - - default: - if (stmt->current_col < 0) - { - pbic = &sbic; - pbic->data_left = -1; - } - else - pbic = &opts->bindings[stmt->current_col]; - if (pbic->data_left < 0) - { - BOOL lf_conv = SC_get_conn(stmt)->connInfo.lf_conversion; -#ifdef UNICODE_SUPPORT - if (fCType == SQL_C_WCHAR) - { - len = utf8_to_ucs2_lf(neut_str, -1, lf_conv, NULL, 0); - len *= 2; - wchanged = changed = TRUE; - } - else -#endif /* UNICODE_SUPPORT */ - /* convert linefeeds to carriage-return/linefeed */ - len = convert_linefeeds(neut_str, NULL, 0, lf_conv, &changed); - if (cbValueMax == 0) /* just returns length - * info */ - { - result = COPY_RESULT_TRUNCATED; - break; - } - if (!pbic->ttlbuf) - pbic->ttlbuflen = 0; - if (changed || len >= cbValueMax) - { - if (len >= (int) pbic->ttlbuflen) - { - pbic->ttlbuf = realloc(pbic->ttlbuf, len + 1); - pbic->ttlbuflen = len + 1; - } -#ifdef UNICODE_SUPPORT - if (fCType == SQL_C_WCHAR) - { - utf8_to_ucs2_lf(neut_str, -1, lf_conv, (SQLWCHAR *) pbic->ttlbuf, len / 2); - } - else -#endif /* UNICODE_SUPPORT */ - convert_linefeeds(neut_str, pbic->ttlbuf, pbic->ttlbuflen, lf_conv, &changed); - ptr = pbic->ttlbuf; - } - else - { - if (pbic->ttlbuf) - { - free(pbic->ttlbuf); - pbic->ttlbuf = NULL; - } - ptr = neut_str; - } - } - else - ptr = pbic->ttlbuf; - - mylog("DEFAULT: len = %d, ptr = '%s'\n", len, ptr); - - if (stmt->current_col >= 0) - { - if (pbic->data_left > 0) - { - ptr += strlen(ptr) - pbic->data_left; - len = pbic->data_left; - } - else - pbic->data_left = len; - } - - if (cbValueMax > 0) - { - copy_len = (len >= cbValueMax) ? cbValueMax - 1 : len; - -#ifdef HAVE_LOCALE_H - switch (field_type) - { - case PG_TYPE_FLOAT4: - case PG_TYPE_FLOAT8: - case PG_TYPE_NUMERIC: - { - struct lconv *lc; - char *new_string; - int i, j; - - new_string = malloc( cbValueMax ); - lc = localeconv(); - for (i = 0, j = 0; ptr[i]; i++) - if (ptr[i] == '.') - { - strncpy(&new_string[j], lc->decimal_point, strlen(lc->decimal_point)); - j += strlen(lc->decimal_point); - } - else - new_string[j++] = ptr[i]; - new_string[j] = '\0'; - strncpy_null(rgbValueBindRow, new_string, copy_len + 1); - free(new_string); - break; - } - default: - /* Copy the data */ - strncpy_null(rgbValueBindRow, ptr, copy_len + 1); - } -#else /* HAVE_LOCALE_H */ - /* Copy the data */ - memcpy(rgbValueBindRow, ptr, copy_len); - rgbValueBindRow[copy_len] = '\0'; -#endif /* HAVE_LOCALE_H */ - - /* Adjust data_left for next time */ - if (stmt->current_col >= 0) - pbic->data_left -= copy_len; - } - - /* - * Finally, check for truncation so that proper status can - * be returned - */ - if (cbValueMax > 0 && len >= cbValueMax) - result = COPY_RESULT_TRUNCATED; - else - { - if (pbic->ttlbuf != NULL) - { - free(pbic->ttlbuf); - pbic->ttlbuf = NULL; - } - } - - - mylog(" SQL_C_CHAR, default: len = %d, cbValueMax = %d, rgbValueBindRow = '%s'\n", len, cbValueMax, rgbValueBindRow); - break; - } -#ifdef UNICODE_SUPPORT - if (SQL_C_WCHAR == fCType && ! wchanged) - { - if (cbValueMax > 2 * len) - { - char *str = strdup(rgbValueBindRow); - UInt4 ucount = utf8_to_ucs2(str, len, (SQLWCHAR *) rgbValueBindRow, cbValueMax / 2); - if (cbValueMax < 2 * (SDWORD) ucount) - result = COPY_RESULT_TRUNCATED; - len = ucount * 2; - free(str); - } - else - { - len *= 2; - result = COPY_RESULT_TRUNCATED; - } - } -#endif /* UNICODE_SUPPORT */ - - } - else - { - /* - * for SQL_C_CHAR, it's probably ok to leave currency symbols in. - * But to convert to numeric types, it is necessary to get rid of - * those. - */ - if (field_type == PG_TYPE_MONEY) - { - if (convert_money(neut_str, midtemp[mtemp_cnt], sizeof(midtemp[0]))) - { - neut_str = midtemp[mtemp_cnt]; - mtemp_cnt++; - } - else - return COPY_UNSUPPORTED_TYPE; - } - - switch (fCType) - { - case SQL_C_DATE: -#if (ODBCVER >= 0x0300) - case SQL_C_TYPE_DATE: /* 91 */ -#endif - len = 6; - { - DATE_STRUCT *ds; - - if (bind_size > 0) - ds = (DATE_STRUCT *) ((char *) rgbValue + (bind_row * bind_size)); - else - ds = (DATE_STRUCT *) rgbValue + bind_row; - ds->year = st.y; - ds->month = st.m; - ds->day = st.d; - } - break; - - case SQL_C_TIME: -#if (ODBCVER >= 0x0300) - case SQL_C_TYPE_TIME: /* 92 */ -#endif - len = 6; - { - TIME_STRUCT *ts; - - if (bind_size > 0) - ts = (TIME_STRUCT *) ((char *) rgbValue + (bind_row * bind_size)); - else - ts = (TIME_STRUCT *) rgbValue + bind_row; - ts->hour = st.hh; - ts->minute = st.mm; - ts->second = st.ss; - } - break; - - case SQL_C_TIMESTAMP: -#if (ODBCVER >= 0x0300) - case SQL_C_TYPE_TIMESTAMP: /* 93 */ -#endif - len = 16; - { - TIMESTAMP_STRUCT *ts; - - if (bind_size > 0) - ts = (TIMESTAMP_STRUCT *) ((char *) rgbValue + (bind_row * bind_size)); - else - ts = (TIMESTAMP_STRUCT *) rgbValue + bind_row; - ts->year = st.y; - ts->month = st.m; - ts->day = st.d; - ts->hour = st.hh; - ts->minute = st.mm; - ts->second = st.ss; - ts->fraction = st.fr; - } - break; - - case SQL_C_BIT: - len = 1; - if (bind_size > 0) - *(UCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str); - else - *((UCHAR *) rgbValue + bind_row) = atoi(neut_str); - - /* - * mylog("SQL_C_BIT: bind_row = %d val = %d, cb = %d, - * rgb=%d\n", bind_row, atoi(neut_str), cbValueMax, - * *((UCHAR *)rgbValue)); - */ - break; - - case SQL_C_STINYINT: - case SQL_C_TINYINT: - len = 1; - if (bind_size > 0) - *(SCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str); - else - *((SCHAR *) rgbValue + bind_row) = atoi(neut_str); - break; - - case SQL_C_UTINYINT: - len = 1; - if (bind_size > 0) - *(UCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str); - else - *((UCHAR *) rgbValue + bind_row) = atoi(neut_str); - break; - - case SQL_C_FLOAT: -#ifdef HAVE_LOCALE_H - strcpy(saved_locale, setlocale(LC_ALL, NULL)); - setlocale(LC_ALL, "C"); -#endif /* HAVE_LOCALE_H */ - len = 4; - if (bind_size > 0) - *(SFLOAT *) ((char *) rgbValue + (bind_row * bind_size)) = (float) atof(neut_str); - else - *((SFLOAT *) rgbValue + bind_row) = (float) atof(neut_str); -#ifdef HAVE_LOCALE_H - setlocale(LC_ALL, saved_locale); -#endif /* HAVE_LOCALE_H */ - break; - - case SQL_C_DOUBLE: -#ifdef HAVE_LOCALE_H - strcpy(saved_locale, setlocale(LC_ALL, NULL)); - setlocale(LC_ALL, "C"); -#endif /* HAVE_LOCALE_H */ - len = 8; - if (bind_size > 0) - *(SDOUBLE *) ((char *) rgbValue + (bind_row * bind_size)) = atof(neut_str); - else - *((SDOUBLE *) rgbValue + bind_row) = atof(neut_str); -#ifdef HAVE_LOCALE_H - setlocale(LC_ALL, saved_locale); -#endif /* HAVE_LOCALE_H */ - break; - -#if (ODBCVER >= 0x0300) - case SQL_C_NUMERIC: -#ifdef HAVE_LOCALE_H - /* strcpy(saved_locale, setlocale(LC_ALL, NULL)); - setlocale(LC_ALL, "C"); not needed currently */ -#endif /* HAVE_LOCALE_H */ - { - SQL_NUMERIC_STRUCT *ns; - int i, nlen, bit, hval, tv, dig, sta, olen; - char calv[SQL_MAX_NUMERIC_LEN * 3], *wv; - BOOL dot_exist; - - len = sizeof(SQL_NUMERIC_STRUCT); - if (bind_size > 0) - ns = (SQL_NUMERIC_STRUCT *) ((char *) rgbValue + (bind_row * bind_size)); - else - ns = (SQL_NUMERIC_STRUCT *) rgbValue + bind_row; - for (wv = neut_str; *wv && isspace(*wv); wv++) - ; - ns->sign = 1; - if (*wv == '-') - { - ns->sign = 0; - wv++; - } - else if (*wv == '+') - wv++; - while (*wv == '0') wv++; - ns->precision = 0; - ns->scale = 0; - for (nlen = 0, dot_exist = FALSE;; wv++) - { - if (*wv == '.') - { - if (dot_exist) - break; - dot_exist = TRUE; - } - else if (!isdigit(*wv)) - break; - else - { - if (dot_exist) - ns->scale++; - else - ns->precision++; - calv[nlen++] = *wv; - } - } - memset(ns->val, 0, sizeof(ns->val)); - for (hval = 0, bit = 1L, sta = 0, olen = 0; sta < nlen;) - { - for (dig = 0, i = sta; i < nlen; i++) - { - tv = dig * 10 + calv[i] - '0'; - dig = tv % 2; - calv[i] = tv / 2 + '0'; - if (i == sta && tv < 2) - sta++; - } - if (dig > 0) - hval |= bit; - bit <<= 1; - if (bit >= (1L << 8)) - { - ns->val[olen++] = hval; - hval = 0; - bit = 1L; - if (olen >= SQL_MAX_NUMERIC_LEN - 1) - { - ns->scale = sta - ns->precision; - break; - } - } - } - if (hval && olen < SQL_MAX_NUMERIC_LEN - 1) - ns->val[olen++] = hval; - } -#ifdef HAVE_LOCALE_H - /* setlocale(LC_ALL, saved_locale); */ -#endif /* HAVE_LOCALE_H */ - break; -#endif /* ODBCVER */ - - case SQL_C_SSHORT: - case SQL_C_SHORT: - len = 2; - if (bind_size > 0) - *(SWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str); - else - *((SWORD *) rgbValue + bind_row) = atoi(neut_str); - break; - - case SQL_C_USHORT: - len = 2; - if (bind_size > 0) - *(UWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(neut_str); - else - *((UWORD *) rgbValue + bind_row) = atoi(neut_str); - break; - - case SQL_C_SLONG: - case SQL_C_LONG: - len = 4; - if (bind_size > 0) - *(SDWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atol(neut_str); - else - *((SDWORD *) rgbValue + bind_row) = atol(neut_str); - break; - - case SQL_C_ULONG: - len = 4; - if (bind_size > 0) - *(UDWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atol(neut_str); - else - *((UDWORD *) rgbValue + bind_row) = atol(neut_str); - break; - -#if (ODBCVER >= 0x0300) && defined(ODBCINT64) -#ifdef WIN32 - case SQL_C_SBIGINT: - len = 8; - if (bind_size > 0) - *(SQLBIGINT *) ((char *) rgbValue + (bind_row * bind_size)) = _atoi64(neut_str); - else - *((SQLBIGINT *) rgbValue + bind_row) = _atoi64(neut_str); - break; - - case SQL_C_UBIGINT: - len = 8; - if (bind_size > 0) - *(SQLUBIGINT *) ((char *) rgbValue + (bind_row * bind_size)) = _atoi64(neut_str); - else - *((SQLUBIGINT *) rgbValue + bind_row) = _atoi64(neut_str); - break; - -#endif /* WIN32 */ -#endif /* ODBCINT64 */ - case SQL_C_BINARY: - - /* truncate if necessary */ - /* convert octal escapes to bytes */ - - if (stmt->current_col < 0) - { - pbic = &sbic; - pbic->data_left = -1; - } - else - pbic = &opts->bindings[stmt->current_col]; - if (!pbic->ttlbuf) - pbic->ttlbuflen = 0; - if (len = strlen(neut_str), len >= (int) pbic->ttlbuflen) - { - pbic->ttlbuf = realloc(pbic->ttlbuf, len + 1); - pbic->ttlbuflen = len + 1; - } - len = convert_from_pgbinary(neut_str, pbic->ttlbuf, pbic->ttlbuflen); - ptr = pbic->ttlbuf; - - if (stmt->current_col >= 0) - { - /* - * Second (or more) call to SQLGetData so move the - * pointer - */ - if (pbic->data_left > 0) - { - ptr += len - pbic->data_left; - len = pbic->data_left; - } - - /* First call to SQLGetData so initialize data_left */ - else - pbic->data_left = len; - - } - - if (cbValueMax > 0) - { - copy_len = (len > cbValueMax) ? cbValueMax : len; - - /* Copy the data */ - memcpy(rgbValueBindRow, ptr, copy_len); - - /* Adjust data_left for next time */ - if (stmt->current_col >= 0) - pbic->data_left -= copy_len; - } - - /* - * Finally, check for truncation so that proper status can - * be returned - */ - if (len > cbValueMax) - result = COPY_RESULT_TRUNCATED; - - if (pbic->ttlbuf) - { - free(pbic->ttlbuf); - pbic->ttlbuf = NULL; - } - mylog("SQL_C_BINARY: len = %d, copy_len = %d\n", len, copy_len); - break; - - default: - return COPY_UNSUPPORTED_TYPE; - } - } - - /* store the length of what was copied, if there's a place for it */ - if (pcbValue) - *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = len; - - if (result == COPY_OK && stmt->current_col >= 0) - opts->bindings[stmt->current_col].data_left = 0; - return result; - -} - - -/*-------------------------------------------------------------------- - * Functions/Macros to get rid of query size limit. - * - * I always used the follwoing macros to convert from - * old_statement to new_statement. Please improve it - * if you have a better way. Hiroshi 2001/05/22 - *-------------------------------------------------------------------- - */ - -#define FLGP_PREPARE_DUMMY_CURSOR 1L -#define FLGP_CURSOR_CHECK_OK (1L << 1) -#define FLGP_SELECT_INTO (1L << 2) -#define FLGP_SELECT_FOR_UPDATE (1L << 3) -typedef struct _QueryParse { - const char *statement; - int statement_type; - UInt4 opos; - int from_pos; - int where_pos; - UInt4 stmt_len; - BOOL in_quote, in_dquote, in_escape; - char token_save[64]; - int token_len; - BOOL prev_token_end; - BOOL proc_no_param; - unsigned int declare_pos; - UInt4 flags; -#ifdef MULTIBYTE - encoded_str encstr; -#endif /* MULTIBYTE */ -} QueryParse; - -static int -QP_initialize(QueryParse *q, const StatementClass *stmt) -{ - q->statement = stmt->statement; - q->statement_type = stmt->statement_type; - q->opos = 0; - q->from_pos = -1; - q->where_pos = -1; - q->stmt_len = (q->statement) ? strlen(q->statement) : -1; - q->in_quote = q->in_dquote = q->in_escape = FALSE; - q->token_save[0] = '\0'; - q->token_len = 0; - q->prev_token_end = TRUE; - q->proc_no_param = TRUE; - q->declare_pos = 0; - q->flags = 0; -#ifdef MULTIBYTE - make_encoded_str(&q->encstr, SC_get_conn(stmt), q->statement); -#endif /* MULTIBYTE */ - - return q->stmt_len; -} - -#define FLGB_PRE_EXECUTING 1L -#define FLGB_INACCURATE_RESULT (1L << 1) -#define FLGB_CREATE_KEYSET (1L << 2) -#define FLGB_KEYSET_DRIVEN (1L << 3) -typedef struct _QueryBuild { - char *query_statement; - UInt4 str_size_limit; - UInt4 str_alsize; - UInt4 npos; - int current_row; - int param_number; - APDFields *apdopts; - UInt4 load_stmt_len; - UInt4 flags; - BOOL lf_conv; - int ccsc; - int errornumber; - const char *errormsg; - - ConnectionClass *conn; /* mainly needed for LO handling */ - StatementClass *stmt; /* needed to set error info in ENLARGE_.. */ -} QueryBuild; - -#define INIT_MIN_ALLOC 4096 -static int -QB_initialize(QueryBuild *qb, UInt4 size, StatementClass *stmt, ConnectionClass *conn) -{ - UInt4 newsize = 0; - - qb->flags = 0; - qb->load_stmt_len = 0; - qb->stmt = stmt; - qb->apdopts = NULL; - if (conn) - qb->conn = conn; - else if (stmt) - { - qb->apdopts = SC_get_APD(stmt); - qb->conn = SC_get_conn(stmt); - if (stmt->pre_executing) - qb->flags |= FLGB_PRE_EXECUTING; - } - else - { - qb->conn = NULL; - return -1; - } - qb->lf_conv = qb->conn->connInfo.lf_conversion; - qb->ccsc = qb->conn->ccsc; - - if (stmt) - qb->str_size_limit = stmt->stmt_size_limit; - else - qb->str_size_limit = -1; - if (qb->str_size_limit > 0) - { - if (size > qb->str_size_limit) - return -1; - newsize = qb->str_size_limit; - } - else - { - newsize = INIT_MIN_ALLOC; - while (newsize <= size) - newsize *= 2; - } - if ((qb->query_statement = malloc(newsize)) == NULL) - { - qb->str_alsize = 0; - return -1; - } - qb->query_statement[0] = '\0'; - qb->str_alsize = newsize; - qb->npos = 0; - qb->current_row = stmt->exec_current_row < 0 ? 0 : stmt->exec_current_row; - qb->param_number = -1; - qb->errornumber = 0; - qb->errormsg = NULL; - - return newsize; -} - -static int -QB_initialize_copy(QueryBuild *qb_to, const QueryBuild *qb_from, UInt4 size) -{ - memcpy(qb_to, qb_from, sizeof(QueryBuild)); - - if (qb_to->str_size_limit > 0) - { - if (size > qb_to->str_size_limit) - return -1; - } - if ((qb_to->query_statement = malloc(size)) == NULL) - { - qb_to->str_alsize = 0; - return -1; - } - qb_to->query_statement[0] = '\0'; - qb_to->str_alsize = size; - qb_to->npos = 0; - - return size; -} - -static void -QB_Destructor(QueryBuild *qb) -{ - if (qb->query_statement) - { - free(qb->query_statement); - qb->query_statement = NULL; - qb->str_alsize = 0; - } -} - -/* - * New macros (Aceto) - *-------------------- - */ - -#define F_OldChar(qp) \ -qp->statement[qp->opos] - -#define F_OldPtr(qp) \ -(qp->statement + qp->opos) - -#define F_OldNext(qp) \ -(++qp->opos) - -#define F_OldPrior(qp) \ -(--qp->opos) - -#define F_OldPos(qp) \ -qp->opos - -#define F_ExtractOldTo(qp, buf, ch, maxsize) \ -do { \ - unsigned int c = 0; \ - while (qp->statement[qp->opos] != '\0' && qp->statement[qp->opos] != ch) \ - { \ - buf[c++] = qp->statement[qp->opos++]; \ - if (c >= maxsize) \ - break; \ - } \ - if (qp->statement[qp->opos] == '\0') \ - return SQL_ERROR; \ - buf[c] = '\0'; \ -} while (0) - -#define F_NewChar(qb) \ -qb->query_statement[qb->npos] - -#define F_NewPtr(qb) \ -(qb->query_statement + qb->npos) - -#define F_NewNext(qb) \ -(++qb->npos) - -#define F_NewPos(qb) \ -(qb->npos) - - -static int -convert_escape(QueryParse *qp, QueryBuild *qb); -static int -inner_process_tokens(QueryParse *qp, QueryBuild *qb); -static int -ResolveOneParam(QueryBuild *qb); -static int -processParameters(QueryParse *qp, QueryBuild *qb, -UInt4 *output_count, Int4 param_pos[][2]); - -static int -enlarge_query_statement(QueryBuild *qb, unsigned int newsize) -{ - unsigned int newalsize = INIT_MIN_ALLOC; - static char *func = "enlarge_statement"; - - if (qb->str_size_limit > 0 && qb->str_size_limit < (int) newsize) - { - free(qb->query_statement); - qb->query_statement = NULL; - qb->str_alsize = 0; - if (qb->stmt) - { - qb->stmt->errormsg = "Query buffer overflow in copy_statement_with_parameters"; - qb->stmt->errornumber = STMT_EXEC_ERROR; - SC_log_error(func, "", qb->stmt); - } - else - { - qb->errormsg = "Query buffer overflow in copy_statement_with_parameters"; - qb->errornumber = STMT_EXEC_ERROR; - } - return -1; - } - while (newalsize <= newsize) - newalsize *= 2; - if (!(qb->query_statement = realloc(qb->query_statement, newalsize))) - { - qb->str_alsize = 0; - if (qb->stmt) - { - qb->stmt->errormsg = "Query buffer allocate error in copy_statement_with_parameters"; - qb->stmt->errornumber = STMT_EXEC_ERROR; - } - else - { - qb->errormsg = "Query buffer allocate error in copy_statement_with_parameters"; - qb->errornumber = STMT_EXEC_ERROR; - } - return 0; - } - qb->str_alsize = newalsize; - return newalsize; -} - -/*---------- - * Enlarge stmt_with_params if necessary. - *---------- - */ -#define ENLARGE_NEWSTATEMENT(qb, newpos) \ - if (newpos >= qb->str_alsize) \ - { \ - if (enlarge_query_statement(qb, newpos) <= 0) \ - return SQL_ERROR; \ - } - -/*---------- - * Terminate the stmt_with_params string with NULL. - *---------- - */ -#define CVT_TERMINATE(qb) \ -do { \ - qb->query_statement[qb->npos] = '\0'; \ -} while (0) - -/*---------- - * Append a data. - *---------- - */ -#define CVT_APPEND_DATA(qb, s, len) \ -do { \ - unsigned int newpos = qb->npos + len; \ - ENLARGE_NEWSTATEMENT(qb, newpos) \ - memcpy(&qb->query_statement[qb->npos], s, len); \ - qb->npos = newpos; \ - qb->query_statement[newpos] = '\0'; \ -} while (0) - -/*---------- - * Append a string. - *---------- - */ -#define CVT_APPEND_STR(qb, s) \ -do { \ - unsigned int len = strlen(s); \ - CVT_APPEND_DATA(qb, s, len); \ -} while (0) - -/*---------- - * Append a char. - *---------- - */ -#define CVT_APPEND_CHAR(qb, c) \ -do { \ - ENLARGE_NEWSTATEMENT(qb, qb->npos + 1); \ - qb->query_statement[qb->npos++] = c; \ -} while (0) - -/*---------- - * Append a binary data. - * Newly reqeuired size may be overestimated currently. - *---------- - */ -#define CVT_APPEND_BINARY(qb, buf, used) \ -do { \ - unsigned int newlimit = qb->npos + 5 * used; \ - ENLARGE_NEWSTATEMENT(qb, newlimit); \ - qb->npos += convert_to_pgbinary(buf, &qb->query_statement[qb->npos], used); \ -} while (0) - -/*---------- - * - *---------- - */ -#define CVT_SPECIAL_CHARS(qb, buf, used) \ -do { \ - int cnvlen = convert_special_chars(buf, NULL, used, qb->lf_conv, qb->ccsc); \ - unsigned int newlimit = qb->npos + cnvlen; \ -\ - ENLARGE_NEWSTATEMENT(qb, newlimit); \ - convert_special_chars(buf, &qb->query_statement[qb->npos], used, qb->lf_conv, qb->ccsc); \ - qb->npos += cnvlen; \ -} while (0) - -/*---------- - * Check if the statement is - * SELECT ... INTO table FROM ..... - * This isn't really a strict check but ... - *---------- - */ -static BOOL -into_table_from(const char *stmt) -{ - if (strnicmp(stmt, "into", 4)) - return FALSE; - stmt += 4; - if (!isspace((unsigned char) *stmt)) - return FALSE; - while (isspace((unsigned char) *(++stmt))); - switch (*stmt) - { - case '\0': - case ',': - case '\'': - return FALSE; - case '\"': /* double quoted table name ? */ - do - { - do - while (*(++stmt) != '\"' && *stmt); - while (*stmt && *(++stmt) == '\"'); - while (*stmt && !isspace((unsigned char) *stmt) && *stmt != '\"') - stmt++; - } - while (*stmt == '\"'); - break; - default: - while (!isspace((unsigned char) *(++stmt))); - break; - } - if (!*stmt) - return FALSE; - while (isspace((unsigned char) *(++stmt))); - if (strnicmp(stmt, "from", 4)) - return FALSE; - return isspace((unsigned char) stmt[4]); -} - -/*---------- - * Check if the statement is - * SELECT ... FOR UPDATE ..... - * This isn't really a strict check but ... - *---------- - */ -static BOOL -table_for_update(const char *stmt, int *endpos) -{ - const char *wstmt = stmt; - - while (isspace((unsigned char) *(++wstmt))); - if (!*wstmt) - return FALSE; - if (strnicmp(wstmt, "update", 6)) - return FALSE; - wstmt += 6; - *endpos = wstmt - stmt; - return !wstmt[0] || isspace((unsigned char) wstmt[0]); -} - -/*---------- - * Check if the statement is - * INSERT INTO ... () VALUES () - * This isn't really a strict check but ... - *---------- - */ -static BOOL -insert_without_target(const char *stmt, int *endpos) -{ - const char *wstmt = stmt; - - while (isspace((unsigned char) *(++wstmt))); - if (!*wstmt) - return FALSE; - if (strnicmp(wstmt, "VALUES", 6)) - return FALSE; - wstmt += 6; - if (!wstmt[0] || !isspace((unsigned char) wstmt[0])) - return FALSE; - while (isspace((unsigned char) *(++wstmt))); - if (*wstmt != '(' || *(++wstmt) != ')') - return FALSE; - wstmt++; - *endpos = wstmt - stmt; - return !wstmt[0] || isspace((unsigned char) wstmt[0]) - || ';' == wstmt[0]; -} - -#ifdef MULTIBYTE -#define my_strchr(conn, s1,c1) pg_mbschr(conn->ccsc, s1,c1) -#else -#define my_strchr(conn, s1,c1) strchr(s1,c1) -#endif -/* - * This function inserts parameters into an SQL statements. - * It will also modify a SELECT statement for use with declare/fetch cursors. - * This function does a dynamic memory allocation to get rid of query size limit! - */ -int -copy_statement_with_parameters(StatementClass *stmt) -{ - static char *func = "copy_statement_with_parameters"; - RETCODE retval; - QueryParse query_org, *qp; - QueryBuild query_crt, *qb; - - char *new_statement; - - BOOL begin_first = FALSE, prepare_dummy_cursor = FALSE; - ConnectionClass *conn = SC_get_conn(stmt); - ConnInfo *ci = &(conn->connInfo); - int current_row; - - if (!stmt->statement) - { - SC_log_error(func, "No statement string", stmt); - return SQL_ERROR; - } - - current_row = stmt->exec_current_row < 0 ? 0 : stmt->exec_current_row; - qp = &query_org; - QP_initialize(qp, stmt); - - if (ci->disallow_premature) - prepare_dummy_cursor = stmt->pre_executing; - if (prepare_dummy_cursor); - qp->flags |= FLGP_PREPARE_DUMMY_CURSOR; - - -#ifdef DRIVER_CURSOR_IMPLEMENT - if (stmt->statement_type != STMT_TYPE_SELECT) - { - stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY; - stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; - } - else if (stmt->options.cursor_type == SQL_CURSOR_FORWARD_ONLY) - stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; - else if (stmt->options.scroll_concurrency != SQL_CONCUR_READ_ONLY) - { - if (stmt->parse_status == STMT_PARSE_NONE) - parse_statement(stmt); - if (stmt->parse_status == STMT_PARSE_FATAL) - { - stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; - return SQL_ERROR; - } - else if (!stmt->updatable) - { - stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; - stmt->options.cursor_type = SQL_CURSOR_STATIC; - } - else - { - qp->from_pos = stmt->from_pos; - qp->where_pos = stmt->where_pos; - } - } -#else - stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; - if (stmt->options.cursor_type == SQL_CURSOR_KEYSET_DRIVEN) - stmt->options.cursor_type = SQL_CURSOR_STATIC; -#endif /* DRIVER_CURSOR_IMPLEMENT */ - - /* If the application hasn't set a cursor name, then generate one */ - if (stmt->cursor_name[0] == '\0') - sprintf(stmt->cursor_name, "SQL_CUR%p", stmt); - if (stmt->stmt_with_params) - { - free(stmt->stmt_with_params); - stmt->stmt_with_params = NULL; - } - qb = &query_crt; - if (QB_initialize(qb, qp->stmt_len, stmt, NULL) < 0) - return SQL_ERROR; - new_statement = qb->query_statement; - - stmt->miscinfo = 0; - /* For selects, prepend a declare cursor to the statement */ - if (stmt->statement_type == STMT_TYPE_SELECT) - { - SC_set_pre_executable(stmt); - if (prepare_dummy_cursor || ci->drivers.use_declarefetch) - { - if (prepare_dummy_cursor) - { - if (!CC_is_in_trans(conn) && PG_VERSION_GE(conn, 7.1)) - { - strcpy(new_statement, "BEGIN;"); - begin_first = TRUE; - } - } - else if (ci->drivers.use_declarefetch) - SC_set_fetchcursor(stmt); - sprintf(new_statement, "%sdeclare %s cursor for ", - new_statement, stmt->cursor_name); - qb->npos = strlen(new_statement); - qp->flags |= FLGP_CURSOR_CHECK_OK; - qp->declare_pos = qb->npos; - } - else if (SQL_CONCUR_READ_ONLY != stmt->options.scroll_concurrency) - { - qb->flags |= FLGB_CREATE_KEYSET; - if (SQL_CURSOR_KEYSET_DRIVEN == stmt->options.cursor_type) - qb->flags |= FLGB_KEYSET_DRIVEN; - } - } - - for (qp->opos = 0; qp->opos < qp->stmt_len; qp->opos++) - { - retval = inner_process_tokens(qp, qb); - if (SQL_ERROR == retval) - { - if (0 == stmt->errornumber) - { - stmt->errornumber = qb->errornumber; - stmt->errormsg = qb->errormsg; - } - SC_log_error(func, "", stmt); - QB_Destructor(qb); - return retval; - } - } - /* make sure new_statement is always null-terminated */ - CVT_TERMINATE(qb); - - new_statement = qb->query_statement; - stmt->statement_type = qp->statement_type; - stmt->inaccurate_result = (0 != (qb->flags & FLGB_INACCURATE_RESULT)); - if (0 != (qp->flags & FLGP_SELECT_INTO)) - { - SC_no_pre_executable(stmt); - SC_no_fetchcursor(stmt); - stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; - } - if (0 != (qp->flags & FLGP_SELECT_FOR_UPDATE)) - { - SC_no_fetchcursor(stmt); - stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY; - } - - if (conn->DriverToDataSource != NULL) - { - int length = strlen(new_statement); - - conn->DriverToDataSource(conn->translation_option, - SQL_CHAR, - new_statement, length, - new_statement, length, NULL, - NULL, 0, NULL); - } - -#ifdef DRIVER_CURSOR_IMPLEMENT - if (!stmt->load_statement && qp->from_pos >= 0) - { - UInt4 npos = qb->load_stmt_len; - - if (0 == npos) - { - npos = qb->npos; - for (; npos > 0; npos--) - { - if (isspace(new_statement[npos - 1])) - continue; - if (';' != new_statement[npos - 1]) - break; - } - if (0 != (qb->flags & FLGB_KEYSET_DRIVEN)) - { - qb->npos = npos; - /* ---------- - * 1st query is for field information - * 2nd query is keyset gathering - */ - CVT_APPEND_STR(qb, " where ctid = '(,)';select ctid, oid from "); - CVT_APPEND_DATA(qb, qp->statement + qp->from_pos + 5, npos - qp->from_pos - 5); - } - } - stmt->load_statement = malloc(npos + 1); - memcpy(stmt->load_statement, qb->query_statement, npos); - stmt->load_statement[npos] = '\0'; - } -#endif /* DRIVER_CURSOR_IMPLEMENT */ - if (prepare_dummy_cursor && SC_is_pre_executable(stmt)) - { - char fetchstr[128]; - - sprintf(fetchstr, ";fetch backward in %s;close %s;", - stmt->cursor_name, stmt->cursor_name); - if (begin_first && CC_is_in_autocommit(conn)) - strcat(fetchstr, "COMMIT;"); - CVT_APPEND_STR(qb, fetchstr); - stmt->inaccurate_result = TRUE; - } - - stmt->stmt_with_params = qb->query_statement; - return SQL_SUCCESS; -} - -static int -inner_process_tokens(QueryParse *qp, QueryBuild *qb) -{ - static char *func = "inner_process_tokens"; - BOOL lf_conv = qb->lf_conv; - - RETCODE retval; - char oldchar; - - if (qp->from_pos == (Int4) qp->opos) - { - CVT_APPEND_STR(qb, ", CTID, OID "); - } - else if (qp->where_pos == (Int4) qp->opos) - { - qb->load_stmt_len = qb->npos; - if (0 != (qb->flags & FLGB_KEYSET_DRIVEN)) - { - CVT_APPEND_STR(qb, "where ctid = '(,)';select CTID, OID from "); - CVT_APPEND_DATA(qb, qp->statement + qp->from_pos + 5, qp->where_pos - qp->from_pos - 5); - } - } -#ifdef MULTIBYTE - oldchar = encoded_byte_check(&qp->encstr, qp->opos); - if (ENCODE_STATUS(qp->encstr) != 0) - { - CVT_APPEND_CHAR(qb, oldchar); - return SQL_SUCCESS; - } - - /* - * From here we are guaranteed to handle a 1-byte character. - */ -#else - oldchar = qp->statement[qp->opos]; -#endif - - if (qp->in_escape) /* escape check */ - { - qp->in_escape = FALSE; - CVT_APPEND_CHAR(qb, oldchar); - return SQL_SUCCESS; - } - else if (qp->in_quote || qp->in_dquote) /* quote/double quote check */ - { - if (oldchar == '\\') - qp->in_escape = TRUE; - else if (oldchar == '\'' && qp->in_quote) - qp->in_quote = FALSE; - else if (oldchar == '\"' && qp->in_dquote) - qp->in_dquote = FALSE; - CVT_APPEND_CHAR(qb, oldchar); - return SQL_SUCCESS; - } - - /* - * From here we are guranteed to be in neither an escape, a quote - * nor a double quote. - */ - /* Squeeze carriage-return/linefeed pairs to linefeed only */ - else if (lf_conv && oldchar == '\r' && qp->opos + 1 < qp->stmt_len && - qp->statement[qp->opos + 1] == '\n') - return SQL_SUCCESS; - - /* - * Handle literals (date, time, timestamp) and ODBC scalar - * functions - */ - else if (oldchar == '{') - { - if (SQL_ERROR == convert_escape(qp, qb)) - { - if (0 == qb->errornumber) - { - qb->errornumber = STMT_EXEC_ERROR; - qb->errormsg = "ODBC escape convert error"; - } - mylog("%s convert_escape error\n", func); - return SQL_ERROR; - } - if (isalnum(F_OldPtr(qp)[1])) - CVT_APPEND_CHAR(qb, ' '); - return SQL_SUCCESS; - } - /* End of an escape sequence */ - else if (oldchar == '}') - { - if (qp->statement_type == STMT_TYPE_PROCCALL) - { - if (qp->proc_no_param) - CVT_APPEND_STR(qb, "()"); - } - else if (!isspace(F_OldPtr(qp)[1])) - CVT_APPEND_CHAR(qb, ' '); - return SQL_SUCCESS; - } - - /* - * Can you have parameter markers inside of quotes? I dont think - * so. All the queries I've seen expect the driver to put quotes - * if needed. - */ - else if (oldchar != '?') - { - if (oldchar == '\'') - qp->in_quote = TRUE; - else if (oldchar == '\\') - qp->in_escape = TRUE; - else if (oldchar == '\"') - qp->in_dquote = TRUE; - else - { - if (isspace((unsigned char) oldchar)) - { - if (!qp->prev_token_end) - { - qp->prev_token_end = TRUE; - qp->token_save[qp->token_len] = '\0'; - if (qp->token_len == 4) - { - if (0 != (qp->flags & FLGP_CURSOR_CHECK_OK) && - into_table_from(&qp->statement[qp->opos - qp->token_len])) - { - qp->flags |= FLGP_SELECT_INTO; - qp->flags &= ~FLGP_CURSOR_CHECK_OK; - qb->flags &= ~FLGB_KEYSET_DRIVEN; - qp->statement_type = STMT_TYPE_CREATE; - memmove(qb->query_statement, qb->query_statement + qp->declare_pos, qb->npos - qp->declare_pos); - qb->npos -= qp->declare_pos; - } - } - else if (qp->token_len == 3) - { - int endpos; - - if (0 != (qp->flags & FLGP_CURSOR_CHECK_OK) && - strnicmp(qp->token_save, "for", 3) == 0 && - table_for_update(&qp->statement[qp->opos], &endpos)) - { - qp->flags |= FLGP_SELECT_FOR_UPDATE; - qp->flags &= ~FLGP_CURSOR_CHECK_OK; - if (qp->flags & FLGP_PREPARE_DUMMY_CURSOR) - { - qb->npos -= 4; - qp->opos += endpos; - } - else - { - memmove(qb->query_statement, qb->query_statement + qp->declare_pos, qb->npos - qp->declare_pos); - qb->npos -= qp->declare_pos; - } - } - } - else if (qp->token_len == 2) - { - int endpos; - - if (STMT_TYPE_INSERT == qp->statement_type && - strnicmp(qp->token_save, "()", 2) == 0 && - insert_without_target(&qp->statement[qp->opos], &endpos)) - { - qb->npos -= 2; - CVT_APPEND_STR(qb, "DEFAULT VALUES"); - qp->opos += endpos; - return SQL_SUCCESS; - } - } - } - } - else if (qp->prev_token_end) - { - qp->prev_token_end = FALSE; - qp->token_save[0] = oldchar; - qp->token_len = 1; - } - else if (qp->token_len + 1 < sizeof(qp->token_save)) - qp->token_save[qp->token_len++] = oldchar; - } - CVT_APPEND_CHAR(qb, oldchar); - return SQL_SUCCESS; - } - - /* - * Its a '?' parameter alright - */ - if (retval = ResolveOneParam(qb), retval < 0) - return retval; - - return SQL_SUCCESS; -} - -#if (ODBCVER >= 0x0300) -static BOOL -ResolveNumericParam(const SQL_NUMERIC_STRUCT *ns, char *chrform) -{ - static int prec[] = {1, 3, 5, 8, 10, 13, 15, 17, 20, 22, 25, 29, 32, 34, 37, 39}; - Int4 i, j, k, ival, vlen, len, newlen; - unsigned char calv[40]; - const unsigned char *val = (const unsigned char *) ns->val; - BOOL next_figure; - - if (0 == ns->precision) - { - strcpy(chrform, "0"); - return TRUE; - } - else if (ns->precision < prec[sizeof(Int4)]) - { - for (i = 0, ival = 0; i < sizeof(Int4) && prec[i] <= ns->precision; i++) - { - ival += (val[i] << (8 * i)); /* ns->val is little endian */ - } - if (0 == ns->scale) - { - if (0 == ns->sign) - ival *= -1; - sprintf(chrform, "%d", ival); - } - else if (ns->scale > 0) - { - Int4 i, div, o1val, o2val; - - for (i = 0, div = 1; i < ns->scale; i++) - div *= 10; - o1val = ival / div; - o2val = ival % div; - if (0 == ns->sign) - o1val *= -1; - sprintf(chrform, "%d.%0.*d", o1val, ns->scale, o2val); - } - return TRUE; - } - - for (i = 0; i < SQL_MAX_NUMERIC_LEN && prec[i] <= ns->precision; i++) - ; - vlen = i; - len = 0; - memset(calv, 0, sizeof(calv)); - for (i = vlen - 1; i >= 0; i--) - { - for (j = len - 1; j >= 0; j--) - { - if (!calv[j]) - continue; - ival = (((Int4)calv[j]) << 8); - calv[j] = (ival % 10); - ival /= 10; - calv[j + 1] += (ival % 10); - ival /= 10; - calv[j + 2] += (ival % 10); - ival /= 10; - calv[j + 3] += ival; - for (k = j;; k++) - { - next_figure = FALSE; - if (calv[k] > 0) - { - if (k >= len) - len = k + 1; - while (calv[k] > 9) - { - calv[k + 1]++; - calv[k] -= 10; - next_figure = TRUE; - } - } - if (k >= j + 3 && !next_figure) - break; - } - } - ival = val[i]; - if (!ival) - continue; - calv[0] += (ival % 10); - ival /= 10; - calv[1] += (ival % 10); - ival /= 10; - calv[2] += ival; - for (j = 0;; j++) - { - next_figure = FALSE; - if (calv[j] > 0) - { - if (j >= len) - len = j + 1; - while (calv[j] > 9) - { - calv[j + 1]++; - calv[j] -= 10; - next_figure = TRUE; - } - } - if (j >= 2 && !next_figure) - break; - } - } - newlen = 0; - if (0 == ns->sign) - chrform[newlen++] = '-'; - for (i = len - 1; i >= ns->scale; i--) - chrform[newlen++] = calv[i] + '0'; - if (ns->scale > 0) - { - chrform[newlen++] = '.'; - for (; i >= 0; i--) - chrform[newlen++] = calv[i] + '0'; - } - chrform[newlen] = '\0'; - return TRUE; -} -#endif /* ODBCVER */ - -/* - * - */ -static int -ResolveOneParam(QueryBuild *qb) -{ - const char *func = "ResolveOneParam"; - - ConnectionClass *conn = qb->conn; - ConnInfo *ci = &(conn->connInfo); - APDFields *opts = qb->apdopts; - - int param_number; - char param_string[128], tmp[256], - cbuf[PG_NUMERIC_MAX_PRECISION * 2]; /* seems big enough to handle the data in this function */ - Int2 param_ctype, param_sqltype; - SIMPLE_TIME st; - time_t t; - struct tm *tim; - SDWORD used; - char *buffer, *buf, *allocbuf; - Oid lobj_oid; - int lobj_fd, retval; - UInt4 offset = opts->param_offset_ptr ? *opts->param_offset_ptr : 0; - UInt4 current_row = qb->current_row; - - /* - * Its a '?' parameter alright - */ - param_number = ++qb->param_number; - - if (param_number >= opts->allocated) - { - if (0 != (qb->flags & FLGB_PRE_EXECUTING)) - { - CVT_APPEND_STR(qb, "NULL"); - qb->flags |= FLGB_INACCURATE_RESULT; - return SQL_SUCCESS; - } - else - { - CVT_APPEND_CHAR(qb, '?'); - return SQL_SUCCESS; - } - } - - /* Assign correct buffers based on data at exec param or not */ - if (opts->parameters[param_number].data_at_exec) - { - used = opts->parameters[param_number].EXEC_used ? *opts->parameters[param_number].EXEC_used : SQL_NTS; - buffer = opts->parameters[param_number].EXEC_buffer; - } - else - { - UInt4 bind_size = opts->param_bind_type; - UInt4 ctypelen; - - buffer = opts->parameters[param_number].buffer + offset; - if (current_row > 0) - { - if (bind_size > 0) - buffer += (bind_size * current_row); - else if (ctypelen = ctype_length(opts->parameters[param_number].CType), ctypelen > 0) - buffer += current_row * ctypelen; - else - buffer += current_row * opts->parameters[param_number].buflen; - } - if (opts->parameters[param_number].used) - { - UInt4 p_offset = offset; - if (bind_size > 0) - p_offset = offset + bind_size * current_row; - else - p_offset = offset + sizeof(SDWORD) * current_row; - used = *(SDWORD *)((char *)opts->parameters[param_number].used + p_offset); - } - else - used = SQL_NTS; - } - - /* Handle NULL parameter data */ - if (used == SQL_NULL_DATA) - { - CVT_APPEND_STR(qb, "NULL"); - return SQL_SUCCESS; - } - - /* - * If no buffer, and it's not null, then what the hell is it? Just - * leave it alone then. - */ - if (!buffer) - { - if (0 != (qb->flags & FLGB_PRE_EXECUTING)) - { - CVT_APPEND_STR(qb, "NULL"); - qb->flags |= FLGB_INACCURATE_RESULT; - return SQL_SUCCESS; - } - else - { - CVT_APPEND_CHAR(qb, '?'); - return SQL_SUCCESS; - } - } - - param_ctype = opts->parameters[param_number].CType; - param_sqltype = opts->parameters[param_number].SQLType; - - mylog("%s: from(fcType)=%d, to(fSqlType)=%d\n", func, - param_ctype, param_sqltype); - - /* replace DEFAULT with something we can use */ - if (param_ctype == SQL_C_DEFAULT) - param_ctype = sqltype_to_default_ctype(param_sqltype); - - allocbuf = buf = NULL; - param_string[0] = '\0'; - cbuf[0] = '\0'; - memset(&st, 0, sizeof(st)); - t = time(NULL); - tim = localtime(&t); - st.m = tim->tm_mon + 1; - st.d = tim->tm_mday; - st.y = tim->tm_year + 1900; - - /* Convert input C type to a neutral format */ - switch (param_ctype) - { - case SQL_C_BINARY: - case SQL_C_CHAR: - buf = buffer; - break; - -#ifdef UNICODE_SUPPORT - case SQL_C_WCHAR: - buf = allocbuf = ucs2_to_utf8((SQLWCHAR *) buffer, used / 2, &used); - used *= 2; - break; -#endif /* UNICODE_SUPPORT */ - - case SQL_C_DOUBLE: - sprintf(param_string, "%.15g", - *((SDOUBLE *) buffer)); - break; - - case SQL_C_FLOAT: - sprintf(param_string, "%.6g", - *((SFLOAT *) buffer)); - break; - - case SQL_C_SLONG: - case SQL_C_LONG: - sprintf(param_string, "%ld", - *((SDWORD *) buffer)); - break; - -#if (ODBCVER >= 0x0300) && defined(ODBCINT64) -#ifdef WIN32 - case SQL_C_SBIGINT: - sprintf(param_string, "%I64d", - *((SQLBIGINT *) buffer)); - break; - - case SQL_C_UBIGINT: - sprintf(param_string, "%I64u", - *((SQLUBIGINT *) buffer)); - break; - -#endif /* WIN32 */ -#endif /* ODBCINT64 */ - case SQL_C_SSHORT: - case SQL_C_SHORT: - sprintf(param_string, "%d", - *((SWORD *) buffer)); - break; - - case SQL_C_STINYINT: - case SQL_C_TINYINT: - sprintf(param_string, "%d", - *((SCHAR *) buffer)); - break; - - case SQL_C_ULONG: - sprintf(param_string, "%lu", - *((UDWORD *) buffer)); - break; - - case SQL_C_USHORT: - sprintf(param_string, "%u", - *((UWORD *) buffer)); - break; - - case SQL_C_UTINYINT: - sprintf(param_string, "%u", - *((UCHAR *) buffer)); - break; - - case SQL_C_BIT: - { - int i = *((UCHAR *) buffer); - - sprintf(param_string, "%d", i ? 1 : 0); - break; - } - - case SQL_C_DATE: -#if (ODBCVER >= 0x0300) - case SQL_C_TYPE_DATE: /* 91 */ -#endif - { - DATE_STRUCT *ds = (DATE_STRUCT *) buffer; - - st.m = ds->month; - st.d = ds->day; - st.y = ds->year; - - break; - } - - case SQL_C_TIME: -#if (ODBCVER >= 0x0300) - case SQL_C_TYPE_TIME: /* 92 */ -#endif - { - TIME_STRUCT *ts = (TIME_STRUCT *) buffer; - - st.hh = ts->hour; - st.mm = ts->minute; - st.ss = ts->second; - - break; - } - - case SQL_C_TIMESTAMP: -#if (ODBCVER >= 0x0300) - case SQL_C_TYPE_TIMESTAMP: /* 93 */ -#endif - { - TIMESTAMP_STRUCT *tss = (TIMESTAMP_STRUCT *) buffer; - - st.m = tss->month; - st.d = tss->day; - st.y = tss->year; - st.hh = tss->hour; - st.mm = tss->minute; - st.ss = tss->second; - st.fr = tss->fraction; - - mylog("m=%d,d=%d,y=%d,hh=%d,mm=%d,ss=%d\n", st.m, st.d, st.y, st.hh, st.mm, st.ss); - - break; - - } -#if (ODBCVER >= 0x0300) - case SQL_C_NUMERIC: - if (ResolveNumericParam((SQL_NUMERIC_STRUCT *) buffer, param_string)) - break; -#endif - default: - /* error */ - qb->errormsg = "Unrecognized C_parameter type in copy_statement_with_parameters"; - qb->errornumber = STMT_NOT_IMPLEMENTED_ERROR; - CVT_TERMINATE(qb); /* just in case */ - return SQL_ERROR; - } - - /* - * Now that the input data is in a neutral format, convert it to - * the desired output format (sqltype) - */ - - switch (param_sqltype) - { - case SQL_CHAR: - case SQL_VARCHAR: - case SQL_LONGVARCHAR: -#ifdef UNICODE_SUPPORT - case SQL_WCHAR: - case SQL_WVARCHAR: - case SQL_WLONGVARCHAR: -#endif /* UNICODE_SUPPORT */ - - CVT_APPEND_CHAR(qb, '\''); /* Open Quote */ - - /* it was a SQL_C_CHAR */ - if (buf) - CVT_SPECIAL_CHARS(qb, buf, used); - - /* it was a numeric type */ - else if (param_string[0] != '\0') - CVT_APPEND_STR(qb, param_string); - - /* it was date,time,timestamp -- use m,d,y,hh,mm,ss */ - else - { - sprintf(tmp, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d", - st.y, st.m, st.d, st.hh, st.mm, st.ss); - - CVT_APPEND_STR(qb, tmp); - } - - CVT_APPEND_CHAR(qb, '\''); /* Close Quote */ - - break; - - case SQL_DATE: -#if (ODBCVER >= 0x0300) - case SQL_TYPE_DATE: /* 91 */ -#endif - if (buf) - { /* copy char data to time */ - my_strcpy(cbuf, sizeof(cbuf), buf, used); - parse_datetime(cbuf, &st); - } - - sprintf(tmp, "'%.4d-%.2d-%.2d'::date", st.y, st.m, st.d); - - CVT_APPEND_STR(qb, tmp); - break; - - case SQL_TIME: -#if (ODBCVER >= 0x0300) - case SQL_TYPE_TIME: /* 92 */ -#endif - if (buf) - { /* copy char data to time */ - my_strcpy(cbuf, sizeof(cbuf), buf, used); - parse_datetime(cbuf, &st); - } - - sprintf(tmp, "'%.2d:%.2d:%.2d'::time", st.hh, st.mm, st.ss); - - CVT_APPEND_STR(qb, tmp); - break; - - case SQL_TIMESTAMP: -#if (ODBCVER >= 0x0300) - case SQL_TYPE_TIMESTAMP: /* 93 */ -#endif - - if (buf) - { - my_strcpy(cbuf, sizeof(cbuf), buf, used); - parse_datetime(cbuf, &st); - } - - /* - * sprintf(tmp, "'%.4d-%.2d-%.2d %.2d:%.2d:%.2d'", st.y, - * st.m, st.d, st.hh, st.mm, st.ss); - */ - tmp[0] = '\''; - /* Time zone stuff is unreliable */ - stime2timestamp(&st, tmp + 1, USE_ZONE, PG_VERSION_GE(conn, 7.2)); - strcat(tmp, "'::timestamp"); - - CVT_APPEND_STR(qb, tmp); - - break; - - case SQL_BINARY: - case SQL_VARBINARY:/* non-ascii characters should be - * converted to octal */ - CVT_APPEND_CHAR(qb, '\''); /* Open Quote */ - - mylog("SQL_VARBINARY: about to call convert_to_pgbinary, used = %d\n", used); - - CVT_APPEND_BINARY(qb, buf, used); - - CVT_APPEND_CHAR(qb, '\''); /* Close Quote */ - - break; - - case SQL_LONGVARBINARY: - - if (opts->parameters[param_number].data_at_exec) - lobj_oid = opts->parameters[param_number].lobj_oid; - else - { - /* begin transaction if needed */ - if (!CC_is_in_trans(conn)) - { - if (!CC_begin(conn)) - { - qb->errormsg = "Could not begin (in-line) a transaction"; - qb->errornumber = STMT_EXEC_ERROR; - return SQL_ERROR; - } - } - - /* store the oid */ - lobj_oid = lo_creat(conn, INV_READ | INV_WRITE); - if (lobj_oid == 0) - { - qb->errornumber = STMT_EXEC_ERROR; - qb->errormsg = "Couldnt create (in-line) large object."; - return SQL_ERROR; - } - - /* store the fd */ - lobj_fd = lo_open(conn, lobj_oid, INV_WRITE); - if (lobj_fd < 0) - { - qb->errornumber = STMT_EXEC_ERROR; - qb->errormsg = "Couldnt open (in-line) large object for writing."; - return SQL_ERROR; - } - - retval = lo_write(conn, lobj_fd, buffer, used); - - lo_close(conn, lobj_fd); - - /* commit transaction if needed */ - if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn)) - { - if (!CC_commit(conn)) - { - qb->errormsg = "Could not commit (in-line) a transaction"; - qb->errornumber = STMT_EXEC_ERROR; - return SQL_ERROR; - } - } - } - - /* - * the oid of the large object -- just put that in for the - * parameter marker -- the data has already been sent to - * the large object - */ - sprintf(param_string, "'%d'", lobj_oid); - CVT_APPEND_STR(qb, param_string); - - break; - - /* - * because of no conversion operator for bool and int4, - * SQL_BIT - */ - /* must be quoted (0 or 1 is ok to use inside the quotes) */ - - case SQL_REAL: - if (buf) - my_strcpy(param_string, sizeof(param_string), buf, used); - sprintf(tmp, "'%s'::float4", param_string); - CVT_APPEND_STR(qb, tmp); - break; - case SQL_FLOAT: - case SQL_DOUBLE: - if (buf) - my_strcpy(param_string, sizeof(param_string), buf, used); - sprintf(tmp, "'%s'::float8", param_string); - CVT_APPEND_STR(qb, tmp); - break; - case SQL_NUMERIC: - if (buf) - { - cbuf[0] = '\''; - my_strcpy(cbuf + 1, sizeof(cbuf) - 3, buf, used); /* 3 = 1('\'') + - * strlen("'") - * + 1('\0') */ - strcat(cbuf, "'"); - } - else - sprintf(cbuf, "'%s'", param_string); - CVT_APPEND_STR(qb, cbuf); - break; - default: /* a numeric type or SQL_BIT */ - if (param_sqltype == SQL_BIT) - CVT_APPEND_CHAR(qb, '\''); /* Open Quote */ - - if (buf) - { - switch (used) - { - case SQL_NULL_DATA: - break; - case SQL_NTS: - CVT_APPEND_STR(qb, buf); - break; - default: - CVT_APPEND_DATA(qb, buf, used); - } - } - else - CVT_APPEND_STR(qb, param_string); - - if (param_sqltype == SQL_BIT) - CVT_APPEND_CHAR(qb, '\''); /* Close Quote */ - - break; - } -#ifdef UNICODE_SUPPORT - if (allocbuf) - free(allocbuf); -#endif /* UNICODE_SUPPORT */ - return SQL_SUCCESS; -} - - -static const char * -mapFunction(const char *func, int param_count) -{ - int i; - - for (i = 0; mapFuncs[i][0]; i++) - { - if (mapFuncs[i][0][0] == '%') - { - if (mapFuncs[i][0][1] - '0' == param_count && - !stricmp(mapFuncs[i][0] + 2, func)) - return mapFuncs[i][1]; - } - else if (!stricmp(mapFuncs[i][0], func)) - return mapFuncs[i][1]; - } - - return NULL; -} - -/* - * processParameters() - * Process function parameters and work with embedded escapes sequences. - */ -static int -processParameters(QueryParse *qp, QueryBuild *qb, - UInt4 *output_count, Int4 param_pos[][2]) -{ - static const char *func = "processParameters"; - int retval, innerParenthesis, param_count; - BOOL stop; - - /* begin with outer '(' */ - innerParenthesis = 0; - param_count = 0; - stop = FALSE; - for (; F_OldPos(qp) < qp->stmt_len; F_OldNext(qp)) - { - retval = inner_process_tokens(qp, qb); - if (retval == SQL_ERROR) - return retval; -#ifdef MULTIBYTE - if (ENCODE_STATUS(qp->encstr) != 0) - continue; -#endif - if (qp->in_dquote || qp->in_quote || qp->in_escape) - continue; - - switch (F_OldChar(qp)) - { - case ',': - if (1 == innerParenthesis) - { - param_pos[param_count][1] = F_NewPos(qb) - 2; - param_count++; - param_pos[param_count][0] = F_NewPos(qb); - param_pos[param_count][1] = -1; - } - break; - case '(': - if (0 == innerParenthesis) - { - param_pos[param_count][0] = F_NewPos(qb); - param_pos[param_count][1] = -1; - } - innerParenthesis++; - break; - - case ')': - innerParenthesis--; - if (0 == innerParenthesis) - { - param_pos[param_count][1] = F_NewPos(qb) - 2; - param_count++; - param_pos[param_count][0] = - param_pos[param_count][1] = -1; - } - if (output_count) - *output_count = F_NewPos(qb); - break; - - case '}': - stop = (0 == innerParenthesis); - break; - - } - if (stop) /* returns with the last } position */ - break; - } - if (param_pos[param_count][0] >= 0) - { - mylog("%s closing ) not found %d\n", func, innerParenthesis); - qb->errornumber = STMT_EXEC_ERROR; - qb->errormsg = "processParameters closing ) not found"; - return SQL_ERROR; - } - else if (1 == param_count) /* the 1 parameter is really valid ? */ - { - BOOL param_exist = FALSE; - int i; - - for (i = param_pos[0][0]; i <= param_pos[0][1]; i++) - { - if (!isspace(qb->query_statement[i])) - { - param_exist = TRUE; - break; - } - } - if (!param_exist) - { - param_pos[0][0] = param_pos[0][1] = -1; - } - } - - return SQL_SUCCESS; -} - -/* - * convert_escape() - * This function doesn't return a pointer to static memory any longer ! - */ -static int -convert_escape(QueryParse *qp, QueryBuild *qb) -{ - static const char *func = "convert_escape"; - RETCODE retval = SQL_SUCCESS; - char buf[1024], key[65]; - unsigned char ucv; - UInt4 prtlen; - - if (F_OldChar(qp) == '{') /* skip the first { */ - F_OldNext(qp); - /* Separate off the key, skipping leading and trailing whitespace */ - while ((ucv = F_OldChar(qp)) != '\0' && isspace(ucv)) - F_OldNext(qp); - /* - * procedure calls - */ - if (qp->statement_type == STMT_TYPE_PROCCALL) - { - int lit_call_len = 4; - ConnectionClass *conn = qb->conn; - - /* '?=' to accept return values exists ? */ - if (F_OldChar(qp) == '?') - { - qb->param_number++; - while (isspace((unsigned char) qp->statement[++qp->opos])); - if (F_OldChar(qp) != '=') - { - F_OldPrior(qp); - return SQL_SUCCESS; - } - while (isspace((unsigned char) qp->statement[++qp->opos])); - } - if (strnicmp(F_OldPtr(qp), "call", lit_call_len) || - !isspace((unsigned char) F_OldPtr(qp)[lit_call_len])) - { - F_OldPrior(qp); - return SQL_SUCCESS; - } - qp->opos += lit_call_len; - CVT_APPEND_STR(qb, "SELECT "); - if (my_strchr(conn, F_OldPtr(qp), '(')) - qp->proc_no_param = FALSE; - return SQL_SUCCESS; - } - - sscanf(F_OldPtr(qp), "%32s", key); - while ((ucv = F_OldChar(qp)) != '\0' && (!isspace(ucv))) - F_OldNext(qp); - while ((ucv = F_OldChar(qp)) != '\0' && isspace(ucv)) - F_OldNext(qp); - - /* Avoid the concatenation of the function name with the previous word. Aceto */ - - if (F_NewPos(qb) > 0 && isalnum(F_NewPtr(qb)[-1])) - CVT_APPEND_CHAR(qb, ' '); - - if (strcmp(key, "d") == 0) - { - /* Literal; return the escape part adding type cast */ - F_ExtractOldTo(qp, buf, '}', sizeof(buf)); - prtlen = snprintf(buf, sizeof(buf), "%s::date ", buf); - CVT_APPEND_DATA(qb, buf, prtlen); - } - else if (strcmp(key, "t") == 0) - { - /* Literal; return the escape part adding type cast */ - F_ExtractOldTo(qp, buf, '}', sizeof(buf)); - prtlen = snprintf(buf, sizeof(buf), "%s::time", buf); - CVT_APPEND_DATA(qb, buf, prtlen); - } - else if (strcmp(key, "ts") == 0) - { - /* Literal; return the escape part adding type cast */ - F_ExtractOldTo(qp, buf, '}', sizeof(buf)); - if (PG_VERSION_LT(qb->conn, 7.1)) - prtlen = snprintf(buf, sizeof(buf), "%s::datetime", buf); - else - prtlen = snprintf(buf, sizeof(buf), "%s::timestamp", buf); - CVT_APPEND_DATA(qb, buf, prtlen); - } - else if (strcmp(key, "oj") == 0) /* {oj syntax support for 7.1 * servers */ - { - F_OldPrior(qp); - return SQL_SUCCESS; /* Continue at inner_process_tokens loop */ - } - else if (strcmp(key, "fn") == 0) - { - QueryBuild nqb; - const char *mapExpr; - int i, param_count; - UInt4 param_consumed; - Int4 param_pos[16][2]; - - /* Separate off the func name, skipping leading and trailing whitespace */ - i = 0; - while ((ucv = F_OldChar(qp)) != '\0' && ucv != '(' && - (!isspace(ucv))) - { - if (i < sizeof(key)-1) - key[i++] = ucv; - F_OldNext(qp); - } - key[i] = '\0'; - while ((ucv = F_OldChar(qp)) != '\0' && isspace(ucv)) - F_OldNext(qp); - - /* - * We expect left parenthesis here, else return fn body as-is - * since it is one of those "function constants". - */ - if (F_OldChar(qp) != '(') - { - CVT_APPEND_STR(qb, key); - return SQL_SUCCESS; - } - - /* - * Process parameter list and inner escape - * sequences - * Aceto 2002-01-29 - */ - - QB_initialize_copy(&nqb, qb, 1024); - if (retval = processParameters(qp, &nqb, ¶m_consumed, param_pos), retval == SQL_ERROR) - { - qb->errornumber = nqb.errornumber; - qb->errormsg = nqb.errormsg; - QB_Destructor(&nqb); - return retval; - } - - for (param_count = 0;; param_count++) - { - if (param_pos[param_count][0] < 0) - break; - } - if (param_count == 1 && - param_pos[0][1] < param_pos[0][0]) - param_count = 0; - - mapExpr = mapFunction(key, param_count); - if (mapExpr == NULL) - { - CVT_APPEND_STR(qb, key); - CVT_APPEND_DATA(qb, nqb.query_statement, nqb.npos); - } - else - { - const char *mapptr; - int from, to, pidx, paramlen; - - for (prtlen = 0, mapptr = mapExpr; *mapptr; mapptr++) - { - if (*mapptr != '$') - { - CVT_APPEND_CHAR(qb, *mapptr); - continue; - } - mapptr++; - if (*mapptr == '*') - { - from = 1; - to = param_consumed - 2; - } - else if (isdigit(*mapptr)) - { - pidx = *mapptr - '0' - 1; - if (pidx < 0 || - param_pos[pidx][0] < 0) - { - qb->errornumber = STMT_EXEC_ERROR; - qb->errormsg = "param not found"; - qlog("%s %dth param not found for the expression %s\n", pidx + 1, mapExpr); - retval = SQL_ERROR; - break; - } - from = param_pos[pidx][0]; - to = param_pos[pidx][1]; - } - else - { - qb->errornumber = STMT_EXEC_ERROR; - qb->errormsg = "internal expression error"; - qlog("%s internal expression error %s\n", func, mapExpr); - retval = SQL_ERROR; - break; - } - paramlen = to - from + 1; - if (paramlen > 0) - CVT_APPEND_DATA(qb, nqb.query_statement+ from, paramlen); - } - } - if (0 == qb->errornumber) - { - qb->errornumber = nqb.errornumber; - qb->errormsg = nqb.errormsg; - } - if (SQL_ERROR != retval) - { - qb->param_number = nqb.param_number; - qb->flags = nqb.flags; - } - QB_Destructor(&nqb); - } - else - { - /* Bogus key, leave untranslated */ - return SQL_ERROR; - } - - return retval; -} - -BOOL -convert_money(const char *s, char *sout, size_t soutmax) -{ - size_t i = 0, - out = 0; - - for (i = 0; s[i]; i++) - { - if (s[i] == '$' || s[i] == ',' || s[i] == ')') - ; /* skip these characters */ - else - { - if (out + 1 >= soutmax) - return FALSE; /* sout is too short */ - if (s[i] == '(') - sout[out++] = '-'; - else - sout[out++] = s[i]; - } - } - sout[out] = '\0'; - return TRUE; -} - - -/* - * This function parses a character string for date/time info and fills in SIMPLE_TIME - * It does not zero out SIMPLE_TIME in case it is desired to initialize it with a value - */ -char -parse_datetime(const char *buf, SIMPLE_TIME *st) -{ - int y, - m, - d, - hh, - mm, - ss; - int nf; - - y = m = d = hh = mm = ss = 0; - st->fr = 0; - st->infinity = 0; - - /* escape sequence ? */ - if (buf[0] == '{') - { - while (*(++buf) && *buf != '\''); - if (!(*buf)) - return FALSE; - buf++; - } - if (buf[4] == '-') /* year first */ - nf = sscanf(buf, "%4d-%2d-%2d %2d:%2d:%2d", &y, &m, &d, &hh, &mm, &ss); - else - nf = sscanf(buf, "%2d-%2d-%4d %2d:%2d:%2d", &m, &d, &y, &hh, &mm, &ss); - - if (nf == 5 || nf == 6) - { - st->y = y; - st->m = m; - st->d = d; - st->hh = hh; - st->mm = mm; - st->ss = ss; - - return TRUE; - } - - if (buf[4] == '-') /* year first */ - nf = sscanf(buf, "%4d-%2d-%2d", &y, &m, &d); - else - nf = sscanf(buf, "%2d-%2d-%4d", &m, &d, &y); - - if (nf == 3) - { - st->y = y; - st->m = m; - st->d = d; - - return TRUE; - } - - nf = sscanf(buf, "%2d:%2d:%2d", &hh, &mm, &ss); - if (nf == 2 || nf == 3) - { - st->hh = hh; - st->mm = mm; - st->ss = ss; - - return TRUE; - } - - return FALSE; -} - - -/* Change linefeed to carriage-return/linefeed */ -int -convert_linefeeds(const char *si, char *dst, size_t max, BOOL convlf, BOOL *changed) -{ - size_t i = 0, - out = 0; - - if (max == 0) - max = 0xffffffff; - *changed = FALSE; - for (i = 0; si[i] && out < max - 1; i++) - { - if (convlf && si[i] == '\n') - { - /* Only add the carriage-return if needed */ - if (i > 0 && si[i - 1] == '\r') - { - if (dst) - dst[out++] = si[i]; - else - out++; - continue; - } - *changed = TRUE; - - if (dst) - { - dst[out++] = '\r'; - dst[out++] = '\n'; - } - else - out += 2; - } - else - { - if (dst) - dst[out++] = si[i]; - else - out++; - } - } - if (dst) - dst[out] = '\0'; - return out; -} - - -/* - * Change carriage-return/linefeed to just linefeed - * Plus, escape any special characters. - */ -int -convert_special_chars(const char *si, char *dst, int used, BOOL convlf, int ccsc) -{ - size_t i = 0, - out = 0, - max; - char *p = NULL; -#ifdef MULTIBYTE - encoded_str encstr; -#endif - - - if (used == SQL_NTS) - max = strlen(si); - else - max = used; - if (dst) - { - p = dst; - p[0] = '\0'; - } -#ifdef MULTIBYTE - encoded_str_constr(&encstr, ccsc, si); -#endif - - for (i = 0; i < max && si[i]; i++) - { -#ifdef MULTIBYTE - encoded_nextchar(&encstr); - if (ENCODE_STATUS(encstr) != 0) - { - if (p) - p[out] = si[i]; - out++; - continue; - } -#endif - if (convlf && si[i] == '\r' && si[i + 1] == '\n') - continue; - else if (si[i] == '\'' || si[i] == '\\') - { - if (p) - p[out++] = '\\'; - else - out++; - } - if (p) - p[out++] = si[i]; - else - out++; - } - if (p) - p[out] = '\0'; - return out; -} - - -/* !!! Need to implement this function !!! */ -int -convert_pgbinary_to_char(const char *value, char *rgbValue, int cbValueMax) -{ - mylog("convert_pgbinary_to_char: value = '%s'\n", value); - - strncpy_null(rgbValue, value, cbValueMax); - return 0; -} - - -static unsigned int -conv_from_octal(const unsigned char *s) -{ - int i, - y = 0; - - for (i = 1; i <= 3; i++) - y += (s[i] - '0') << (3 * (3 - i)); - - return y; - -} - - -static unsigned int -conv_from_hex(const unsigned char *s) -{ - int i, - y = 0, - val; - - for (i = 1; i <= 2; i++) - { - if (s[i] >= 'a' && s[i] <= 'f') - val = s[i] - 'a' + 10; - else if (s[i] >= 'A' && s[i] <= 'F') - val = s[i] - 'A' + 10; - else - val = s[i] - '0'; - - y += val << (4 * (2 - i)); - } - - return y; -} - - -/* convert octal escapes to bytes */ -int -convert_from_pgbinary(const unsigned char *value, unsigned char *rgbValue, int cbValueMax) -{ - size_t i, - ilen = strlen(value); - int o = 0; - - - for (i = 0; i < ilen;) - { - if (value[i] == '\\') - { - if (value[i + 1] == '\\') - { - rgbValue[o] = value[i]; - i += 2; - } - else - { - rgbValue[o] = conv_from_octal(&value[i]); - i += 4; - } - } - else - rgbValue[o] = value[i++]; - mylog("convert_from_pgbinary: i=%d, rgbValue[%d] = %d, %c\n", i, o, rgbValue[o], rgbValue[o]); - o++; - } - - rgbValue[o] = '\0'; /* extra protection */ - - return o; -} - - -static char * -conv_to_octal(unsigned char val) -{ - int i; - static char x[6]; - - x[0] = '\\'; - x[1] = '\\'; - x[5] = '\0'; - - for (i = 4; i > 1; i--) - { - x[i] = (val & 7) + '0'; - val >>= 3; - } - - return x; -} - - -/* convert non-ascii bytes to octal escape sequences */ -int -convert_to_pgbinary(const unsigned char *in, char *out, int len) -{ - int i, - o = 0; - - for (i = 0; i < len; i++) - { - mylog("convert_to_pgbinary: in[%d] = %d, %c\n", i, in[i], in[i]); - if (isalnum(in[i]) || in[i] == ' ') - out[o++] = in[i]; - else - { - strcpy(&out[o], conv_to_octal(in[i])); - o += 5; - } - } - - mylog("convert_to_pgbinary: returning %d, out='%.*s'\n", o, o, out); - - return o; -} - - -void -encode(const char *in, char *out) -{ - unsigned int i, - ilen = strlen(in), - o = 0; - - for (i = 0; i < ilen; i++) - { - if (in[i] == '+') - { - sprintf(&out[o], "%%2B"); - o += 3; - } - else if (isspace((unsigned char) in[i])) - out[o++] = '+'; - else if (!isalnum((unsigned char) in[i])) - { - sprintf(&out[o], "%%%02x", (unsigned char) in[i]); - o += 3; - } - else - out[o++] = in[i]; - } - out[o++] = '\0'; -} - - -void -decode(const char *in, char *out) -{ - unsigned int i, - ilen = strlen(in), - o = 0; - - for (i = 0; i < ilen; i++) - { - if (in[i] == '+') - out[o++] = ' '; - else if (in[i] == '%') - { - sprintf(&out[o++], "%c", conv_from_hex(&in[i])); - i += 2; - } - else - out[o++] = in[i]; - } - out[o++] = '\0'; -} - -static const char *hextbl = "0123456789ABCDEF"; -static int -pg_bin2hex(UCHAR *src, UCHAR *dst, int length) -{ - UCHAR chr, - *src_wk, - *dst_wk; - BOOL backwards; - int i; - - backwards = FALSE; - if (dst < src) - { - if (dst + length > src + 1) - return -1; - } - else if (dst < src + length) - backwards = TRUE; - if (backwards) - { - for (i = 0, src_wk = src + length - 1, dst_wk = dst + 2 * length - 1; i < length; i++, src_wk--) - { - chr = *src_wk; - *dst_wk-- = hextbl[chr % 16]; - *dst_wk-- = hextbl[chr >> 4]; - } - } - else - { - for (i = 0, src_wk = src, dst_wk = dst; i < length; i++, src_wk++) - { - chr = *src_wk; - *dst_wk++ = hextbl[chr >> 4]; - *dst_wk++ = hextbl[chr % 16]; - } - } - dst[2 * length] = '\0'; - return length; -} - -/*------- - * 1. get oid (from 'value') - * 2. open the large object - * 3. read from the large object (handle multiple GetData) - * 4. close when read less than requested? -OR- - * lseek/read each time - * handle case where application receives truncated and - * decides not to continue reading. - * - * CURRENTLY, ONLY LONGVARBINARY is handled, since that is the only - * data type currently mapped to a PG_TYPE_LO. But, if any other types - * are desired to map to a large object (PG_TYPE_LO), then that would - * need to be handled here. For example, LONGVARCHAR could possibly be - * mapped to PG_TYPE_LO someday, instead of PG_TYPE_TEXT as it is now. - *------- - */ -int -convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue, - SDWORD cbValueMax, SDWORD *pcbValue) -{ - Oid oid; - int retval, - result, - left = -1; - BindInfoClass *bindInfo = NULL; - ConnectionClass *conn = SC_get_conn(stmt); - ConnInfo *ci = &(conn->connInfo); - ARDFields *opts = SC_get_ARD(stmt); - int factor = (fCType == SQL_C_CHAR ? 2 : 1); - - /* If using SQLGetData, then current_col will be set */ - if (stmt->current_col >= 0) - { - bindInfo = &opts->bindings[stmt->current_col]; - left = bindInfo->data_left; - } - - /* - * if this is the first call for this column, open the large object - * for reading - */ - - if (!bindInfo || bindInfo->data_left == -1) - { - /* begin transaction if needed */ - if (!CC_is_in_trans(conn)) - { - if (!CC_begin(conn)) - { - stmt->errormsg = "Could not begin (in-line) a transaction"; - stmt->errornumber = STMT_EXEC_ERROR; - return COPY_GENERAL_ERROR; - } - } - - oid = atoi(value); - stmt->lobj_fd = lo_open(conn, oid, INV_READ); - if (stmt->lobj_fd < 0) - { - stmt->errornumber = STMT_EXEC_ERROR; - stmt->errormsg = "Couldnt open large object for reading."; - return COPY_GENERAL_ERROR; - } - - /* Get the size */ - retval = lo_lseek(conn, stmt->lobj_fd, 0L, SEEK_END); - if (retval >= 0) - { - left = lo_tell(conn, stmt->lobj_fd); - if (bindInfo) - bindInfo->data_left = left; - - /* return to beginning */ - lo_lseek(conn, stmt->lobj_fd, 0L, SEEK_SET); - } - } - mylog("lo data left = %d\n", left); - - if (left == 0) - return COPY_NO_DATA_FOUND; - - if (stmt->lobj_fd < 0) - { - stmt->errornumber = STMT_EXEC_ERROR; - stmt->errormsg = "Large object FD undefined for multiple read."; - return COPY_GENERAL_ERROR; - } - - retval = lo_read(conn, stmt->lobj_fd, (char *) rgbValue, factor > 1 ? (cbValueMax - 1) / factor : cbValueMax); - if (retval < 0) - { - lo_close(conn, stmt->lobj_fd); - - /* commit transaction if needed */ - if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn)) - { - if (!CC_commit(conn)) - { - stmt->errormsg = "Could not commit (in-line) a transaction"; - stmt->errornumber = STMT_EXEC_ERROR; - return COPY_GENERAL_ERROR; - } - } - - stmt->lobj_fd = -1; - - stmt->errornumber = STMT_EXEC_ERROR; - stmt->errormsg = "Error reading from large object."; - return COPY_GENERAL_ERROR; - } - - if (factor > 1) - pg_bin2hex((char *) rgbValue, (char *) rgbValue, retval); - if (retval < left) - result = COPY_RESULT_TRUNCATED; - else - result = COPY_OK; - - if (pcbValue) - *pcbValue = left < 0 ? SQL_NO_TOTAL : left * factor; - - if (bindInfo && bindInfo->data_left > 0) - bindInfo->data_left -= retval; - - if (!bindInfo || bindInfo->data_left == 0) - { - lo_close(conn, stmt->lobj_fd); - - /* commit transaction if needed */ - if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn)) - { - if (!CC_commit(conn)) - { - stmt->errormsg = "Could not commit (in-line) a transaction"; - stmt->errornumber = STMT_EXEC_ERROR; - return COPY_GENERAL_ERROR; - } - } - - stmt->lobj_fd = -1; /* prevent further reading */ - } - - return result; -} |