diff --git a/src/interfaces/odbc/bind.c b/src/interfaces/odbc/bind.c index d176acee6b..e9f5cc3482 100644 --- a/src/interfaces/odbc/bind.c +++ b/src/interfaces/odbc/bind.c @@ -424,6 +424,8 @@ create_empty_bindings(int num_columns) new_bindings[i].buffer = NULL; new_bindings[i].used = NULL; new_bindings[i].data_left = -1; + new_bindings[i].ttlbuf = NULL; + new_bindings[i].ttlbuflen = 0; } return new_bindings; diff --git a/src/interfaces/odbc/bind.h b/src/interfaces/odbc/bind.h index 444d30f2d2..363abb059b 100644 --- a/src/interfaces/odbc/bind.h +++ b/src/interfaces/odbc/bind.h @@ -22,6 +22,8 @@ struct BindInfoClass_ char *buffer; /* pointer to the buffer */ Int4 *used; /* used space in the buffer (for strings * not counting the '\0') */ + char *ttlbuf; /* to save the large result */ + Int4 ttlbuflen; /* the buffer length */ Int2 returntype; /* kind of conversion to be applied when * returning (SQL_C_DEFAULT, * SQL_C_CHAR...) */ diff --git a/src/interfaces/odbc/columninfo.c b/src/interfaces/odbc/columninfo.c index d9a22a163a..7fe72d3f6d 100644 --- a/src/interfaces/odbc/columninfo.c +++ b/src/interfaces/odbc/columninfo.c @@ -12,6 +12,7 @@ *------- */ +#include "pgtypes.h" #include "columninfo.h" #include "connection.h" @@ -95,7 +96,16 @@ CI_read_fields(ColumnInfoClass *self, ConnectionClass *conn) new_atttypmod = (Int4) SOCK_get_int(sock, 4); /* Subtract the header length */ - new_atttypmod -= 4; + switch (new_adtid) + { + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP_NO_TMZONE: + case PG_TYPE_TIME: + case PG_TYPE_TIME_WITH_TMZONE: + break; + default: + new_atttypmod -= 4; + } if (new_atttypmod < 0) new_atttypmod = -1; diff --git a/src/interfaces/odbc/convert.c b/src/interfaces/odbc/convert.c index 871a9c3741..f284b16df9 100644 --- a/src/interfaces/odbc/convert.c +++ b/src/interfaces/odbc/convert.c @@ -38,6 +38,11 @@ #include "connection.h" #include "pgapifunc.h" +#ifdef __CYGWIN__ +#define TIMEZONE_GLOBAL _timezone +#elif defined(WIN32) || defined(HAVE_INT_TIMEZONE) +#define TIMEZONE_GLOBAL timezone +#endif /* * How to map ODBC scalar functions {fn func(args)} to Postgres. @@ -136,6 +141,156 @@ static char *conv_to_octal(unsigned char val); +/* + * 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; + long timediff; + BOOL withZone = *bZone; + + *bZone = FALSE; + *zone = 0; + st->fr = 0; + if ((scnt = sscanf(str, "%4d-%2d-%2d %2d:%2d:%2d%s", &st->y, &st->m, &st->d, &st->hh, &st->mm, &st->ss, rest)) < 6) + return FALSE; + else if (scnt == 6) + return TRUE; + switch (rest[0]) + { + case '+': + *bZone = TRUE; + *zone = atoi(&rest[1]); + break; + case '-': + *bZone = TRUE; + *zone = -atoi(&rest[1]); + break; + case '.': + if ((ptr = strchr(rest, '+')) != NULL) + { + *bZone = TRUE; + *zone = atoi(&ptr[1]); + *ptr = '\0'; + } + else if ((ptr = strchr(rest, '-')) != NULL) + { + *bZone = TRUE; + *zone = -atoi(&ptr[1]); + *ptr = '\0'; + } + for (i = 1; i < 10; i++) + { + if (!isdigit(rest[i])) + break; + } + for (; i < 10; i++) + rest[i] = '0'; + rest[i] = '\0'; + st->fr = atoi(&rest[1]); + break; + default: + return TRUE; + } + if (!withZone || !*bZone || st->y < 1970) + return TRUE; +#if defined(WIN32) || defined(HAVE_INT_TIMEZONE) + if (!tzname[0] || !tzname[0][0]) + { + *bZone = FALSE; + return TRUE; + } + timediff = TIMEZONE_GLOBAL + (*zone) * 3600; + if (!daylight && timediff == 0) /* the same timezone */ + return TRUE; + else + { + struct tm tm, *tm2; + time_t time0; + + *bZone = FALSE; + tm.tm_year = st->y - 1900; + tm.tm_mon = st->m - 1; + tm.tm_mday = st->d; + tm.tm_hour = st->hh; + tm.tm_min = st->mm; + tm.tm_sec = st->ss; + tm.tm_isdst = -1; + time0 = mktime(&tm); + if (time0 < 0) + return TRUE; + if (tm.tm_isdst > 0) + timediff -= 3600; + if (timediff == 0) /* the same time zone */ + return TRUE; + time0 -= timediff; + if (time0 >= 0 && (tm2 = localtime(&time0)) != NULL) + { + st->y = tm2->tm_year + 1900; + st->m = tm2->tm_mon + 1; + st->d = tm2->tm_mday; + st->hh= tm2->tm_hour; + st->mm= tm2->tm_min; + st->ss= tm2->tm_sec; + *bZone = TRUE; + } + } +#endif /* WIN32 */ + return TRUE; +} + +static BOOL stime2timestamp(const SIMPLE_TIME *st, char * str, BOOL bZone, BOOL precision) +{ + char precstr[16], zonestr[16]; + int i; + + precstr[0] = '\0'; + if (precision && st->fr) + { + sprintf(precstr, ".%09d", st->fr); + for (i = 9; i > 0; i--) + { + if (precstr[i] != '0') + break; + precstr[i] = '\0'; + } + } + zonestr[0] = '\0'; +#if defined(WIN32) || defined(HAVE_INT_TIMEZONE) + if (bZone && tzname[0] && tzname[0][0] && st->y >= 1970) + { + long zoneint; + struct tm tm; + time_t time0; + + zoneint = TIMEZONE_GLOBAL; + if (daylight && st->y >=1900) + { + tm.tm_year = st->y - 1900; + tm.tm_mon = st->m - 1; + tm.tm_mday = st->d; + tm.tm_hour = st->hh; + tm.tm_min = st->mm; + tm.tm_sec = st->ss; + tm.tm_isdst = -1; + time0 = mktime(&tm); + if (time0 >= 0 && tm.tm_isdst > 0) + zoneint -= 3600; + } + if (zoneint > 0) + sprintf(zonestr, "-%02d", (int)zoneint / 3600); + else + sprintf(zonestr, "+%02d", -(int)zoneint / 3600); + } +#endif /* WIN32 */ + sprintf(str, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d%s%s", st->y, st->m, st->d, st->hh, st->mm, st->ss, precstr, zonestr); + return TRUE; +} + /* This is called by SQLFetch() */ int copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col) @@ -165,14 +320,29 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 int bind_size = stmt->options.bind_size; int result = COPY_OK; BOOL changed; - static char *tempBuf = NULL; - static unsigned int tempBuflen = 0; const char *neut_str = value; char midtemp[2][32]; int mtemp_cnt = 0; + static BindInfoClass sbic; + BindInfoClass *pbic; - if (!tempBuf) - tempBuflen = 0; + if (stmt->current_col >= 0) + { + pbic = &stmt->bindings[stmt->current_col]; + if (pbic->data_left == -2) + pbic->data_left = (cbValueMax > 0) ? 0 : -1; /* This seems to be * needed for ADO ? */ + if (pbic->data_left == 0) + { + if (pbic->ttlbuf != NULL) + { + free(pbic->ttlbuf); + pbic->ttlbuf = NULL; + pbic->ttlbuflen = 0; + } + pbic->data_left = -2; /* needed by ADO ? */ + return COPY_NO_DATA_FOUND; + } + } /*--------- * rgbValueOffset is *ONLY* for character and binary data. * pcbValueOffset is for computing any pcbValue location @@ -243,8 +413,15 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 case PG_TYPE_ABSTIME: case PG_TYPE_DATETIME: case PG_TYPE_TIMESTAMP: + st.fr = 0; if (strnicmp(value, "invalid", 7) != 0) - sscanf(value, "%4d-%2d-%2d %2d:%2d:%2d", &st.y, &st.m, &st.d, &st.hh, &st.mm, &st.ss); + { + BOOL bZone = (field_type != PG_TYPE_TIMESTAMP_NO_TMZONE && PG_VERSION_GE(SC_get_conn(stmt), 7.2)); + int zone; + /*sscanf(value, "%4d-%2d-%2d %2d:%2d:%2d", &st.y, &st.m, &st.d, &st.hh, &st.mm, &st.ss);*/ + bZone = FALSE; /* time zone stuff is unreliable */ + timestamp2stime(value, &st, &bZone, &zone); + } else { /* @@ -421,10 +598,14 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 break; default: - if (stmt->current_col >= 0 && stmt->bindings[stmt->current_col].data_left == -2) - stmt->bindings[stmt->current_col].data_left = (cbValueMax > 0) ? 0 : -1; /* This seems to be - * needed for ADO ? */ - if (stmt->current_col < 0 || stmt->bindings[stmt->current_col].data_left < 0) + if (stmt->current_col < 0) + { + pbic = &sbic; + pbic->data_left = -1; + } + else + pbic = &stmt->bindings[stmt->current_col]; + if (pbic->data_left < 0) { /* convert linefeeds to carriage-return/linefeed */ len = convert_linefeeds(neut_str, NULL, 0, &changed); @@ -434,51 +615,42 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 result = COPY_RESULT_TRUNCATED; break; } + if (!pbic->ttlbuf) + pbic->ttlbuflen = 0; if (changed || len >= cbValueMax) { - if (len >= (int) tempBuflen) + if (len >= (int) pbic->ttlbuflen) { - tempBuf = realloc(tempBuf, len + 1); - tempBuflen = len + 1; + pbic->ttlbuf = realloc(pbic->ttlbuf, len + 1); + pbic->ttlbuflen = len + 1; } - convert_linefeeds(neut_str, tempBuf, tempBuflen, &changed); - ptr = tempBuf; + convert_linefeeds(neut_str, pbic->ttlbuf, pbic->ttlbuflen, &changed); + ptr = pbic->ttlbuf; } else { - if (tempBuf) + if (pbic->ttlbuf) { - free(tempBuf); - tempBuf = NULL; + free(pbic->ttlbuf); + pbic->ttlbuf = NULL; } ptr = neut_str; } } else - ptr = tempBuf; + ptr = pbic->ttlbuf; mylog("DEFAULT: len = %d, ptr = '%s'\n", len, ptr); if (stmt->current_col >= 0) { - if (stmt->bindings[stmt->current_col].data_left == 0) + if (pbic->data_left > 0) { - if (tempBuf) - { - free(tempBuf); - tempBuf = NULL; - } - /* The following seems to be needed for ADO ? */ - stmt->bindings[stmt->current_col].data_left = -2; - return COPY_NO_DATA_FOUND; - } - else if (stmt->bindings[stmt->current_col].data_left > 0) - { - ptr += strlen(ptr) - stmt->bindings[stmt->current_col].data_left; - len = stmt->bindings[stmt->current_col].data_left; + ptr += strlen(ptr) - pbic->data_left; + len = pbic->data_left; } else - stmt->bindings[stmt->current_col].data_left = len; + pbic->data_left = len; } if (cbValueMax > 0) @@ -491,7 +663,7 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 /* Adjust data_left for next time */ if (stmt->current_col >= 0) - stmt->bindings[stmt->current_col].data_left -= copy_len; + pbic->data_left -= copy_len; } /* @@ -502,10 +674,10 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 result = COPY_RESULT_TRUNCATED; else { - if (tempBuf) + if (pbic->ttlbuf != NULL) { - free(tempBuf); - tempBuf = NULL; + free(pbic->ttlbuf); + pbic->ttlbuf = NULL; } } @@ -537,6 +709,9 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 switch (fCType) { case SQL_C_DATE: +#if (ODBCVER >= 0x0300) + case SQL_C_TYPE_DATE: /* 91 */ +#endif len = 6; { DATE_STRUCT *ds; @@ -552,6 +727,9 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 break; case SQL_C_TIME: +#if (ODBCVER >= 0x0300) + case SQL_C_TYPE_TIME: /* 92 */ +#endif len = 6; { TIME_STRUCT *ts; @@ -567,6 +745,9 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 break; case SQL_C_TIMESTAMP: +#if (ODBCVER >= 0x0300) + case SQL_C_TYPE_TIMESTAMP: /* 93 */ +#endif len = 16; { TIMESTAMP_STRUCT *ts; @@ -581,7 +762,7 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 ts->hour = st.hh; ts->minute = st.mm; ts->second = st.ss; - ts->fraction = 0; + ts->fraction = st.fr; } break; @@ -671,37 +852,38 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 /* truncate if necessary */ /* convert octal escapes to bytes */ - if (len = strlen(neut_str), len >= (int) tempBuflen) + if (stmt->current_col < 0) { - tempBuf = realloc(tempBuf, len + 1); - tempBuflen = len + 1; + pbic = &sbic; + pbic->data_left = -1; } - len = convert_from_pgbinary(neut_str, tempBuf, tempBuflen); - ptr = tempBuf; + else + pbic = &stmt->bindings[stmt->current_col]; + if (!pbic->ttlbuf) + pbic->ttlbuflen = 0; + if (len = strlen(neut_str), len >= (int) pbic->ttlbuflen) + { + pbic->ttlbuf = realloc(pbic->ttlbuf, len + 1); + pbic->ttlbuflen = len + 1; + } + len = convert_from_pgbinary(neut_str, pbic->ttlbuf, pbic->ttlbuflen); + ptr = pbic->ttlbuf; if (stmt->current_col >= 0) { - /* No more data left for this column */ - if (stmt->bindings[stmt->current_col].data_left == 0) - { - free(tempBuf); - tempBuf = NULL; - return COPY_NO_DATA_FOUND; - } - /* * Second (or more) call to SQLGetData so move the * pointer */ - else if (stmt->bindings[stmt->current_col].data_left > 0) + if (pbic->data_left > 0) { - ptr += len - stmt->bindings[stmt->current_col].data_left; - len = stmt->bindings[stmt->current_col].data_left; + ptr += len - pbic->data_left; + len = pbic->data_left; } /* First call to SQLGetData so initialize data_left */ else - stmt->bindings[stmt->current_col].data_left = len; + pbic->data_left = len; } @@ -714,7 +896,7 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 /* Adjust data_left for next time */ if (stmt->current_col >= 0) - stmt->bindings[stmt->current_col].data_left -= copy_len; + pbic->data_left -= copy_len; } /* @@ -724,10 +906,10 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 if (len > cbValueMax) result = COPY_RESULT_TRUNCATED; - if (tempBuf) + if (pbic->ttlbuf) { - free(tempBuf); - tempBuf = NULL; + free(pbic->ttlbuf); + pbic->ttlbuf = NULL; } mylog("SQL_C_BINARY: len = %d, copy_len = %d\n", len, copy_len); break; @@ -741,6 +923,8 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 if (pcbValue) *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = len; + if (result == COPY_OK && stmt->current_col >=0) + stmt->bindings[stmt->current_col].data_left = 0; return result; } @@ -1406,6 +1590,9 @@ copy_statement_with_parameters(StatementClass *stmt) } case SQL_C_DATE: +#if (ODBCVER >= 0x0300) + case SQL_C_TYPE_DATE: /* 91 */ +#endif { DATE_STRUCT *ds = (DATE_STRUCT *) buffer; @@ -1417,6 +1604,9 @@ copy_statement_with_parameters(StatementClass *stmt) } case SQL_C_TIME: +#if (ODBCVER >= 0x0300) + case SQL_C_TYPE_TIME: /* 92 */ +#endif { TIME_STRUCT *ts = (TIME_STRUCT *) buffer; @@ -1428,6 +1618,9 @@ copy_statement_with_parameters(StatementClass *stmt) } case SQL_C_TIMESTAMP: +#if (ODBCVER >= 0x0300) + case SQL_C_TYPE_TIMESTAMP: /* 93 */ +#endif { TIMESTAMP_STRUCT *tss = (TIMESTAMP_STRUCT *) buffer; @@ -1437,6 +1630,7 @@ copy_statement_with_parameters(StatementClass *stmt) 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); @@ -1487,6 +1681,9 @@ copy_statement_with_parameters(StatementClass *stmt) break; case SQL_DATE: +#if (ODBCVER >= 0x0300) + case SQL_TYPE_DATE: /* 91 */ +#endif if (buf) { /* copy char data to time */ my_strcpy(cbuf, sizeof(cbuf), buf, used); @@ -1499,6 +1696,9 @@ copy_statement_with_parameters(StatementClass *stmt) break; case SQL_TIME: +#if (ODBCVER >= 0x0300) + case SQL_TYPE_TIME: /* 92 */ +#endif if (buf) { /* copy char data to time */ my_strcpy(cbuf, sizeof(cbuf), buf, used); @@ -1511,6 +1711,9 @@ copy_statement_with_parameters(StatementClass *stmt) break; case SQL_TIMESTAMP: +#if (ODBCVER >= 0x0300) + case SQL_TYPE_TIMESTAMP: /* 93 */ +#endif if (buf) { @@ -1518,8 +1721,12 @@ copy_statement_with_parameters(StatementClass *stmt) parse_datetime(cbuf, &st); } - sprintf(tmp, "'%.4d-%.2d-%.2d %.2d:%.2d:%.2d'", - st.y, st.m, st.d, st.hh, st.mm, st.ss); + /* 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, FALSE, PG_VERSION_GE(conn, 7.2)); + strcat(tmp, "'"); CVT_APPEND_STR(tmp); @@ -2203,7 +2410,43 @@ decode(const char *in, char *out) out[o++] = '\0'; } - +static const char *hextbl = "0123456789ABCDEF"; +static int +pg_bin2hex(UCHAR *src, UCHAR *dst, int length) +{ + UCHAR chr, *src_wk, *dst_wk; + BOOL backwards; + int i; + + backwards = FALSE; + if (dst < src) + { + if (dst + length > src + 1) + return -1; + } + else if (dst < src + length) + backwards = TRUE; + if (backwards) + { + for (i = 0, src_wk = src + length - 1, dst_wk = dst + 2 * length - 1; i < length; i++, src_wk--) + { + chr = *src_wk; + *dst_wk-- = hextbl[chr % 16]; + *dst_wk-- = hextbl[chr >> 4]; + } + } + else + { + for (i = 0, src_wk = src, dst_wk = dst; i < length; i++, src_wk++) + { + chr = *src_wk; + *dst_wk++ = hextbl[chr >> 4]; + *dst_wk++ = hextbl[chr % 16]; + } + } + dst[2 * length] = '\0'; + return length; +} /*------- * 1. get oid (from 'value') * 2. open the large object @@ -2231,6 +2474,7 @@ convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue, BindInfoClass *bindInfo = NULL; ConnectionClass *conn = SC_get_conn(stmt); ConnInfo *ci = &(conn->connInfo); + int factor = (fCType == SQL_C_CHAR ? 2 : 1); /* If using SQLGetData, then current_col will be set */ if (stmt->current_col >= 0) @@ -2304,7 +2548,7 @@ convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue, return COPY_GENERAL_ERROR; } - retval = lo_read(conn, stmt->lobj_fd, (char *) rgbValue, cbValueMax); + retval = lo_read(conn, stmt->lobj_fd, (char *) rgbValue, factor > 1 ? (cbValueMax - 1) / factor : cbValueMax); if (retval < 0) { lo_close(conn, stmt->lobj_fd); @@ -2341,13 +2585,15 @@ convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue, 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; + *pcbValue = left < 0 ? SQL_NO_TOTAL : left * factor; if (bindInfo && bindInfo->data_left > 0) bindInfo->data_left -= retval; diff --git a/src/interfaces/odbc/convert.h b/src/interfaces/odbc/convert.h index dfbd32ce24..2257fb2779 100644 --- a/src/interfaces/odbc/convert.h +++ b/src/interfaces/odbc/convert.h @@ -27,7 +27,8 @@ typedef struct int hh; int mm; int ss; -} SIMPLE_TIME; + int fr; +} SIMPLE_TIME; int copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col); int copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType, diff --git a/src/interfaces/odbc/info.c b/src/interfaces/odbc/info.c index 09439b2a5e..23291b5554 100644 --- a/src/interfaces/odbc/info.c +++ b/src/interfaces/odbc/info.c @@ -216,32 +216,6 @@ PGAPI_GetInfo( case SQL_DRIVER_ODBC_VER: p = DRIVER_ODBC_VER; -#ifdef DRIVER_CURSOR_IMPLEMENT - { - static char dver[32]; - - SQLGetPrivateProfileString(DBMS_NAME, - "DriverODBCVer", "", dver, sizeof(dver), "odbcinst.ini"); - if (dver[0]) - { - int major, - minor; - - mylog("REGISTRY_ODBC_VER = %s\n", dver) - ; - if (sscanf(dver, "%x.%x", &major, &minor) >= 2) - { - Int2 drv_ver = (major << 8) + minor; - - if (drv_ver > ODBCVER) - { - conn->driver_version = drv_ver; - p = dver; - } - } - } - } -#endif /* DRIVER_CURSOR_IMPLEMENT */ break; case SQL_DRIVER_VER: /* ODBC 1.0 */ @@ -842,6 +816,7 @@ PGAPI_GetFunctions( if (fFunction == SQL_API_ALL_FUNCTIONS) { +#if (ODBCVER < 0x0300) if (ci->drivers.lie) { int i; @@ -856,6 +831,7 @@ PGAPI_GetFunctions( pfExists[i] = TRUE; } else +#endif { memset(pfExists, 0, sizeof(UWORD) * 100); @@ -2685,7 +2661,7 @@ PGAPI_PrimaryKeys( /* * Multibyte support stuff for SQLForeignKeys(). * There may be much more effective way in the - * future version. The way is very forcive currently. + * future version. The way is very forcible currently. */ static BOOL isMultibyte(const unsigned char *str) diff --git a/src/interfaces/odbc/odbcapi30.c b/src/interfaces/odbc/odbcapi30.c index 5a63748bf5..9de10a9109 100644 --- a/src/interfaces/odbc/odbcapi30.c +++ b/src/interfaces/odbc/odbcapi30.c @@ -18,8 +18,8 @@ *------- */ -#ifndef ODBCVER_REP -#define ODBCVER_REP 0x0300 +#ifndef ODBCVER +#define ODBCVER 0x0300 #endif #include "psqlodbc.h" #include @@ -344,7 +344,7 @@ SQLGetStmtAttr(HSTMT StatementHandle, len = 4; break; case SQL_ATTR_AUTO_IPD: /* 10001 */ - case SQL_ATTR_ROW_BIND_TYPE: /* == SQL_BIND_TYPE */ + /* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */ case SQL_ATTR_PARAMSET_SIZE: /* 22 */ case SQL_ATTR_PARAM_STATUS_PTR: /* 20 */ case SQL_ATTR_PARAMS_PROCESSED_PTR: /* 21 */ @@ -484,7 +484,7 @@ SQLSetStmtAttr(HSTMT StatementHandle, case SQL_ATTR_APP_ROW_DESC: /* 10010 */ case SQL_ATTR_APP_PARAM_DESC: /* 10011 */ case SQL_ATTR_AUTO_IPD: /* 10001 */ - /* case SQL_ATTR_ROW_BIND_TYPE: */ /* == SQL_BIND_TYPE */ + /* case SQL_ATTR_ROW_BIND_TYPE: ** == SQL_BIND_TYPE(ODBC2.0) */ case SQL_ATTR_IMP_ROW_DESC: /* 10012 */ case SQL_ATTR_IMP_PARAM_DESC: /* 10013 */ case SQL_ATTR_METADATA_ID: /* 10014 */ diff --git a/src/interfaces/odbc/pgtypes.c b/src/interfaces/odbc/pgtypes.c index 1f177a10c4..0a7bd35b6e 100644 --- a/src/interfaces/odbc/pgtypes.c +++ b/src/interfaces/odbc/pgtypes.c @@ -536,6 +536,67 @@ getCharPrecision(StatementClass *stmt, Int4 type, int col, int handle_unknown_si return p; } +static Int2 +getTimestampScale(StatementClass *stmt, Int4 type, int col) +{ + ConnectionClass *conn = SC_get_conn (stmt); + Int4 atttypmod; + QResultClass *result; + ColumnInfoClass *flds; + + mylog("getTimestampScale: type=%d, col=%d\n", type, col); + + if (col < 0) + return 0; + if (PG_VERSION_LT(conn, 7.2)) + return 0; + + result = SC_get_Result(stmt); + + /* + * Manual Result Sets -- use assigned column width (i.e., from + * set_tuplefield_string) + */ + atttypmod = 0; + if (stmt->manual_result) + { + flds = result->fields; + if (flds) + atttypmod = flds->atttypmod[col]; +mylog("atttypmod1=%d\n", atttypmod); + } + else + atttypmod = QR_get_atttypmod(result, col); +mylog("atttypmod2=%d\n", atttypmod); + return (atttypmod > -1 ? atttypmod : 0); +} + + +static Int4 +getTimestampPrecision(StatementClass *stmt, Int4 type, int col) +{ + Int4 fixed, scale; + + mylog("getTimestampPrecision: type=%d, col=%d\n", type, col); + + switch (type) + { + case PG_TYPE_TIME: + fixed = 8; + break; + case PG_TYPE_TIME_WITH_TMZONE: + fixed = 11; + break; + case PG_TYPE_TIMESTAMP_NO_TMZONE: + fixed = 19; + break; + default: + fixed = 22; + break; + } + scale = getTimestampScale(stmt, type, col); + return (scale > 0) ? fixed + 1 + scale : fixed; +} /* * For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, PG_TYPE_NUMERIC, SQLColumns will @@ -591,7 +652,8 @@ pgtype_precision(StatementClass *stmt, Int4 type, int col, int handle_unknown_si case PG_TYPE_TIMESTAMP: return 22; case PG_TYPE_DATETIME: - return 22; + /* return 22; */ + return getTimestampPrecision(stmt, type, col); case PG_TYPE_BOOL: return 1; @@ -728,7 +790,8 @@ pgtype_scale(StatementClass *stmt, Int4 type, int col) case PG_TYPE_TIMESTAMP: return 0; case PG_TYPE_DATETIME: - return 0; + /* return 0; */ + return getTimestampScale(stmt, type, col); case PG_TYPE_NUMERIC: return getNumericScale(stmt, type, col); diff --git a/src/interfaces/odbc/pgtypes.h b/src/interfaces/odbc/pgtypes.h index 7bd33cf751..0e6a1cc897 100644 --- a/src/interfaces/odbc/pgtypes.h +++ b/src/interfaces/odbc/pgtypes.h @@ -58,8 +58,10 @@ #define PG_TYPE_VARCHAR 1043 #define PG_TYPE_DATE 1082 #define PG_TYPE_TIME 1083 +#define PG_TYPE_TIMESTAMP_NO_TMZONE 1114 /* since 7.2 */ #define PG_TYPE_DATETIME 1184 -#define PG_TYPE_TIMESTAMP 1296 +#define PG_TYPE_TIME_WITH_TMZONE 1266 /* since 7.1 */ +#define PG_TYPE_TIMESTAMP 1296 /* deprecated since 7.0 */ #define PG_TYPE_NUMERIC 1700 /* extern Int4 pgtypes_defined[]; */ diff --git a/src/interfaces/odbc/psqlodbc.h b/src/interfaces/odbc/psqlodbc.h index 540e295d56..62e6b9b23a 100644 --- a/src/interfaces/odbc/psqlodbc.h +++ b/src/interfaces/odbc/psqlodbc.h @@ -5,7 +5,7 @@ * * Comments: See "notice.txt" for copyright and license information. * - * $Id: psqlodbc.h,v 1.53 2001/10/28 06:26:14 momjian Exp $ + * $Id: psqlodbc.h,v 1.54 2001/11/05 09:46:17 inoue Exp $ * */ @@ -21,9 +21,7 @@ #include /* for FILE* pointers: see GLOBAL_VALUES */ /* Must come before sql.h */ -#ifdef ODBCVER_REP -#define ODBCVER ODBCVER_REP -#else +#ifndef ODBCVER #define ODBCVER 0x0250 #endif /* ODBCVER_REP */ @@ -77,18 +75,27 @@ typedef UInt4 Oid; #endif /* Driver stuff */ -#define DRIVER_ODBC_VER "02.50" #define DRIVERNAME "PostgreSQL ODBC" +#if (ODBCVER >= 0x0300) +#define DRIVER_ODBC_VER "03.00" +#define DBMS_NAME "PostgreSQL30" +#else +#define DRIVER_ODBC_VER "02.50" #define DBMS_NAME "PostgreSQL" +#endif /* ODBCVER */ -#define POSTGRESDRIVERVERSION "07.01.0008" +#define POSTGRESDRIVERVERSION "07.01.0009" #ifdef WIN32 +#if (ODBCVER >= 0x0300) +#define DRIVER_FILE_NAME "PSQLODBC30.DLL" +#else #define DRIVER_FILE_NAME "PSQLODBC.DLL" +#endif /* ODBCVER */ #else #define DRIVER_FILE_NAME "libpsqlodbc.so" -#endif +#endif /* WIN32 */ /* Limits */ #ifdef WIN32 diff --git a/src/interfaces/odbc/psqlodbc.rc b/src/interfaces/odbc/psqlodbc.rc index a6c9b23bc9..30a7ff7203 100644 --- a/src/interfaces/odbc/psqlodbc.rc +++ b/src/interfaces/odbc/psqlodbc.rc @@ -354,8 +354,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 7,1,0,8 - PRODUCTVERSION 7,1,0,8 + FILEVERSION 7,1,0,9 + PRODUCTVERSION 7,1,0,9 FILEFLAGSMASK 0x3L #ifdef _DEBUG FILEFLAGS 0x1L @@ -377,14 +377,14 @@ BEGIN VALUE "CompanyName", "Insight Distribution Systems\0" #endif VALUE "FileDescription", "PostgreSQL Driver\0" - VALUE "FileVersion", " 07.01.0008\0" + VALUE "FileVersion", " 07.01.0009\0" VALUE "InternalName", "psqlodbc\0" VALUE "LegalCopyright", "\0" VALUE "LegalTrademarks", "ODBC(TM) is a trademark of Microsoft Corporation. Microsoft® is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation.\0" VALUE "OriginalFilename", "psqlodbc.dll\0" VALUE "PrivateBuild", "\0" VALUE "ProductName", "Microsoft Open Database Connectivity\0" - VALUE "ProductVersion", " 07.01.0008\0" + VALUE "ProductVersion", " 07.01.0009\0" VALUE "SpecialBuild", "\0" END END diff --git a/src/interfaces/odbc/statement.c b/src/interfaces/odbc/statement.c index b4e33d0592..b279850678 100644 --- a/src/interfaces/odbc/statement.c +++ b/src/interfaces/odbc/statement.c @@ -320,7 +320,15 @@ SC_Destructor(StatementClass *self) */ /* about that here. */ if (self->bindings) + { + int lf; + for (lf = 0; lf < self->bindings_allocated; lf++) + { + if (self->bindings[lf].ttlbuf != NULL) + free(self->bindings[lf].ttlbuf); + } free(self->bindings); + } /* Free the parsed table information */ if (self->ti) diff --git a/src/interfaces/odbc/win32.mak b/src/interfaces/odbc/win32.mak index 0158adbfa3..6d83b1c7aa 100644 --- a/src/interfaces/odbc/win32.mak +++ b/src/interfaces/odbc/win32.mak @@ -84,7 +84,6 @@ CLEAN : -@erase "$(INTDIR)\tuple.obj" -@erase "$(INTDIR)\tuplelist.obj" -@erase "$(INTDIR)\odbcapi.obj" - -@erase "$(INTDIR)\odbcapi30.obj" -@erase "$(INTDIR)\vc60.idb" -@erase "$(OUTDIR)\psqlodbc.dll" -@erase "$(OUTDIR)\psqlodbc.exp" @@ -169,7 +168,6 @@ LINK32_OBJS= \ "$(INTDIR)\tuple.obj" \ "$(INTDIR)\tuplelist.obj" \ "$(INTDIR)\odbcapi.obj" \ - "$(INTDIR)\odbcapi30.obj" \ "$(INTDIR)\psqlodbc.res" "$(OUTDIR)\psqlodbc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) @@ -219,7 +217,6 @@ CLEAN : -@erase "$(INTDIR)\tuple.obj" -@erase "$(INTDIR)\tuplelist.obj" -@erase "$(INTDIR)\odbcapi.obj" - -@erase "$(INTDIR)\odbcapi30.obj" -@erase "$(INTDIR)\vc60.idb" -@erase "$(INTDIR)\vc60.pdb" -@erase "$(OUTDIR)\psqlodbc.dll" @@ -307,7 +304,6 @@ LINK32_OBJS= \ "$(INTDIR)\tuple.obj" \ "$(INTDIR)\tuplelist.obj" \ "$(INTDIR)\odbcapi.obj" \ - "$(INTDIR)\odbcapi30.obj" \ "$(INTDIR)\psqlodbc.res" "$(OUTDIR)\psqlodbc.dll" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS) @@ -496,11 +492,6 @@ SOURCE=odbcapi.c $(CPP) $(CPP_PROJ) $(SOURCE) -SOURCE=odbcapi30.c - -"$(INTDIR)\odbcapi30.obj" : $(SOURCE) "$(INTDIR)" - $(CPP) $(CPP_PROJ) $(SOURCE) - !ENDIF