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