From 6e45dcb08093d81f244ad6321cbd529d0a7efe61 Mon Sep 17 00:00:00 2001 From: Hiroshi Inoue Date: Sat, 21 Aug 2010 06:29:20 +0000 Subject: [PATCH] 1.Introduce pgtype_attr_xxxx functions which take a typmod parameter as well as a type oid parameter as an extension of pgtype_xxxx functions so that SQLColumns and SQLDescribeCol(SQLColAttrinute) could use common functions. 2.Call PQconnectdbParams instead of PQconnectdb when it's available. 3.Make cursor open check at transaction end a little more effective. 4.Added code for SQL_INTERVAL support and refcursor support though they are disabled. --- bind.c | 26 +- bind.h | 2 +- configure.ac | 50 ++ connection.c | 146 ++-- convert.c | 304 +++++++- convert.h | 10 +- dlg_specific.h | 1 + environ.c | 11 + environ.h | 2 + execute.c | 2 +- info.c | 122 ++-- loadlib.c | 103 ++- loadlib.h | 2 + pgtypes.c | 1853 ++++++++++++++++++++++++++++++++---------------- pgtypes.h | 76 +- qresult.c | 26 +- qresult.h | 6 +- results.c | 43 +- statement.c | 40 +- version.h | 8 +- 20 files changed, 2031 insertions(+), 802 deletions(-) diff --git a/bind.c b/bind.c index 4c29e29..1939f74 100644 --- a/bind.c +++ b/bind.c @@ -96,6 +96,12 @@ PGAPI_BindParameter( if (ibScale > 0) ipdopts->parameters[ipar].precision = ibScale; break; + case SQL_C_INTERVAL_DAY_TO_SECOND: + case SQL_C_INTERVAL_HOUR_TO_SECOND: + case SQL_C_INTERVAL_MINUTE_TO_SECOND: + case SQL_C_INTERVAL_SECOND: + ipdopts->parameters[ipar].precision = 6; + break; } apdopts->parameters[ipar].precision = ipdopts->parameters[ipar].precision; apdopts->parameters[ipar].scale = ipdopts->parameters[ipar].scale; @@ -265,12 +271,22 @@ inolog("Bind column 0 is type %d not of type SQL_C_BOOKMARK", fCType); opts->bindings[icol].used = opts->bindings[icol].indicator = pcbValue; opts->bindings[icol].returntype = fCType; + opts->bindings[icol].precision = 0; #if (ODBCVER >= 0x0300) - if (SQL_C_NUMERIC == fCType) - opts->bindings[icol].precision = 32; - else + switch (fCType) + { + case SQL_C_NUMERIC: + opts->bindings[icol].precision = 32; + break; + case SQL_C_TIMESTAMP: + case SQL_C_INTERVAL_DAY_TO_SECOND: + case SQL_C_INTERVAL_HOUR_TO_SECOND: + case SQL_C_INTERVAL_MINUTE_TO_SECOND: + case SQL_C_INTERVAL_SECOND: + opts->bindings[icol].precision = 6; + break; + } #endif /* ODBCVER */ - opts->bindings[icol].precision = 0; opts->bindings[icol].scale = 0; mylog(" bound buffer[%d] = %p\n", icol, opts->bindings[icol].buffer); @@ -394,7 +410,7 @@ inolog("[%d].SQLType=%d .PGType=%d\n", ipar, ipdopts->parameters[ipar].SQLType, } if (pfNullable) - *pfNullable = pgtype_nullable(stmt, ipdopts->parameters[ipar].paramType); + *pfNullable = pgtype_nullable(SC_get_conn(stmt), ipdopts->parameters[ipar].paramType); cleanup: #undef return if (stmt->internal) diff --git a/bind.h b/bind.h index 9aee864..3a2d318 100644 --- a/bind.h +++ b/bind.h @@ -92,7 +92,7 @@ typedef struct /* Macros to handle pgtype of parameters */ #define PIC_get_pgtype(pari) ((pari).PGType) #define PIC_set_pgtype(pari, type) ((pari).PGType = (type)) -#define PIC_dsp_pgtype(stmt, pari) ((pari).PGType ? (pari).PGType : sqltype_to_pgtype(stmt, (pari).SQLType)) +#define PIC_dsp_pgtype(conn, pari) ((pari).PGType ? (pari).PGType : sqltype_to_pgtype(conn, (pari).SQLType)) void extend_column_bindings(ARDFields *opts, int num_columns); void reset_a_column_binding(ARDFields *opts, int icol); diff --git a/configure.ac b/configure.ac index ce516f3..09ba604 100644 --- a/configure.ac +++ b/configure.ac @@ -168,6 +168,28 @@ PGAC_ARG_BOOL(enable, openssl, yes, AM_CONDITIONAL(enable_openssl, [test x"$enable_openssl" = xyes]) +# +# GSSAPI support +# + +PGAC_ARG_BOOL(enable, gss, no, + [ --disable-gss do not build GSSAPI support], + [AC_DEFINE(USE_GSS, 1, + [Define to 1 to build with GSSAPI support (--enable-gss)])]) + +AM_CONDITIONAL(enable_gss, [test x"$enable_gss" = xyes]) + +# +# GKerberos 5 support +# + +PGAC_ARG_BOOL(enable, krb5, no, + [ --disable-krb5 do not build Kerberos5 support], + [AC_DEFINE(USE_KRB5, 1, + [Define to 1 to build with Kerberos 5 support (--enable-krb5)])]) + +AM_CONDITIONAL(enable_krb5, [test x"$enable_krb5" = xyes]) + # # Pthreads # @@ -236,6 +258,14 @@ if test "$enable_openssl" = yes; then AC_CHECK_LIB(ssl, SSL_read, [], [AC_MSG_ERROR([ssl library not found])]) fi +if test "$enable_gss" = yes; then + AC_SEARCH_LIBS(gss_init_sec_context, [gssapi_krb5 gss 'gssapi -lkrb5 -lcrypto'], [], [AC_MSG_ERROR([gssapi library not found])]) +fi + +if test "$enable_krb5" = yes; then + AC_SEARCH_LIBS(krb5_sendauth, [krb5 'krb5 -lcrypto -ldes -lasn1 -lroken'], [], [AC_MSG_ERROR([krb5 library not found])]) + AC_SEARCH_LIBS(com_err, [krb5 'krb5 -lcrypto -ldes -lasn1 -lroken' comerr 'comerr -lssl -lcrypto'], [], [AC_MSG_ERROR([comerr library not found])]) +fi # 3. Header files @@ -246,6 +276,13 @@ fi if test "$enable_openssl" = yes; then AC_CHECK_HEADER([openssl/ssl.h],,[AC_MSG_ERROR([ssl header not found])]) fi +if test "$enable_gss" = yes; then + AC_CHECK_HEADERS([gssapi/gssapi.h], [], + [AC_CHECK_HEADERS([gssapi.h], [], [AC_MSG_ERROR([gssapi header not found])])]) +fi +if test "$enable_krb5" = yes; then + AC_CHECK_HEADER([krb5.h],,[AC_MSG_ERROR([krb5 header not found])]) +fi AC_HEADER_TIME @@ -294,6 +331,19 @@ if test "$enable_pthreads" = yes; then [Define if you have PTHREAD_MUTEX_RECURSIVE_NP])])]) fi fi +if test "$enable_krb5" = yes; then + AC_CHECK_MEMBERS(krb5_error.text.data, [], + [AC_CHECK_MEMBERS(krb5_error.e_data, [], + [AC_MSG_ERROR([could not determine how to extract Kerberos 5 error messages])], + [#include ])], + [#include ]) + AC_MSG_CHECKING(for krb5_free_unparsed_name) + AC_TRY_LINK([#include ], + [krb5_free_unparsed_name(NULL,NULL);], + [AC_DEFINE(HAVE_KRB5_FREE_UNPARSED_NAME, 1, [Define to 1 if you have krb5_free_unparsed_name]) +AC_MSG_RESULT(yes)], + [AC_MSG_RESULT(no)]) +fi # 8. Libltdl AC_CHECK_LIB(ltdl, lt_dlopen) diff --git a/connection.c b/connection.c index 47eef47..b13238c 100644 --- a/connection.c +++ b/connection.c @@ -947,6 +947,7 @@ handle_notice_message(ConnectionClass *self, char *msgbuf, size_t buflen, char * while (truncated) truncated = SOCK_get_string(sock, msgbuffer, sizeof(msgbuffer)); } +mylog("notice message len=%d\n", strlen(msgbuf)); } else { @@ -1033,7 +1034,7 @@ inolog("parameter name=%s\n", msgbuffer); inolog("parameter value=%s\n", msgbuffer); } -static int protocol3_opts_array(ConnectionClass *self, const char *opts[][2], BOOL libpqopt, int dim_opts) +static int protocol3_opts_array(ConnectionClass *self, const char *opts[], const char *vals[], BOOL libpqopt, int dim_opts) { ConnInfo *ci = &(self->connInfo); const char *enc = NULL; @@ -1042,21 +1043,21 @@ static int protocol3_opts_array(ConnectionClass *self, const char *opts[][2], BO cnt = 0; if (libpqopt && ci->server[0]) { - opts[cnt][0] = "host"; opts[cnt++][1] = ci->server; + opts[cnt] = "host"; vals[cnt++] = ci->server; } if (libpqopt && ci->port[0]) { - opts[cnt][0] = "port"; opts[cnt++][1] = ci->port; + opts[cnt] = "port"; vals[cnt++] = ci->port; } if (ci->database[0]) { if (libpqopt) { - opts[cnt][0] = "dbname"; opts[cnt++][1] = ci->database; + opts[cnt] = "dbname"; vals[cnt++] = ci->database; } else { - opts[cnt][0] = "database"; opts[cnt++][1] = ci->database; + opts[cnt] = "database"; vals[cnt++] = ci->database; } } if (ci->username[0] || !libpqopt) @@ -1066,7 +1067,7 @@ static int protocol3_opts_array(ConnectionClass *self, const char *opts[][2], BO DWORD namesize = sizeof(ci->username) - 2; #endif /* WIN32 */ - opts[cnt][0] = "user"; + opts[cnt] = "user"; if (!usrname[0]) { #ifdef WIN32 @@ -1075,7 +1076,7 @@ static int protocol3_opts_array(ConnectionClass *self, const char *opts[][2], BO #endif /* WIN32 */ } mylog("!!! usrname=%s server=%s\n", usrname, ci->server); - opts[cnt++][1] = usrname; + vals[cnt++] = usrname; } if (libpqopt) { @@ -1084,19 +1085,19 @@ mylog("!!! usrname=%s server=%s\n", usrname, ci->server); case '\0': break; case SSLLBYTE_VERIFY: - opts[cnt][0] = "sslmode"; + opts[cnt] = "sslmode"; if (ssl_verify_available()) { switch (ci->sslmode[1]) { case 'f': - opts[cnt++][1] = SSLMODE_VERIFY_FULL; + vals[cnt++] = SSLMODE_VERIFY_FULL; break; case 'c': - opts[cnt++][1] = SSLMODE_VERIFY_CA; + vals[cnt++] = SSLMODE_VERIFY_CA; break; default: - opts[cnt++][1] = ci->sslmode; + vals[cnt++] = ci->sslmode; } } else @@ -1109,42 +1110,45 @@ mylog("!!! usrname=%s server=%s\n", usrname, ci->server); } break; default: - opts[cnt][0] = "sslmode"; - opts[cnt++][1] = ci->sslmode; + opts[cnt] = "sslmode"; + vals[cnt++] = ci->sslmode; } if (ci->password[0]) { - opts[cnt][0] = "password"; opts[cnt++][1] = ci->password; + opts[cnt] = "password"; vals[cnt++] = ci->password; } if (ci->gssauth_use_gssapi) { - opts[cnt][0] = "gsslib"; opts[cnt++][1] = "gssapi"; + opts[cnt] = "gsslib"; vals[cnt++] = "gssapi"; } } else + if (connect_with_param_available()) { /* DateStyle */ - opts[cnt][0] = "DateStyle"; opts[cnt++][1] = "ISO"; + opts[cnt] = "DateStyle"; vals[cnt++] = "ISO"; /* extra_float_digits */ - opts[cnt][0] = "extra_float_digits"; opts[cnt++][1] = "2"; + opts[cnt] = "extra_float_digits"; vals[cnt++] = "2"; /* geqo */ - opts[cnt][0] = "geqo"; + opts[cnt] = "geqo"; if (ci->drivers.disable_optimizer) - opts[cnt++][1] = "off"; + vals[cnt++] = "off"; else - opts[cnt++][1] = "on"; + vals[cnt++] = "on"; /* client_encoding */ enc = get_environment_encoding(self, self->original_client_encoding, NULL, TRUE); if (enc) { mylog("startup client_encoding=%s\n", enc); - opts[cnt][0] = "client_encoding"; opts[cnt++][1] = enc; + opts[cnt] = "client_encoding"; vals[cnt++] = enc; } } + opts[cnt] = vals[cnt] = NULL; return cnt; } +#define PROTOCOL3_OPTS_MAX 20 static int protocol3_packet_build(ConnectionClass *self) { CSTR func = "protocol3_packet_build"; @@ -1152,18 +1156,18 @@ static int protocol3_packet_build(ConnectionClass *self) size_t slen; char *packet, *ppacket; ProtocolVersion pversion; - const char *opts[20][2]; + const char *opts[PROTOCOL3_OPTS_MAX], *vals[PROTOCOL3_OPTS_MAX]; int cnt, i; - cnt = protocol3_opts_array(self, opts, FALSE, sizeof(opts) / sizeof(opts[0])); + cnt = protocol3_opts_array(self, opts, vals, FALSE, sizeof(opts) / sizeof(opts[0])); if (cnt < 0) return 0; slen = sizeof(ProtocolVersion); for (i = 0; i < cnt; i++) { - slen += (strlen(opts[i][0]) + 1); - slen += (strlen(opts[i][1]) + 1); + slen += (strlen(opts[i]) + 1); + slen += (strlen(vals[i]) + 1); } slen++; @@ -1185,10 +1189,10 @@ static int protocol3_packet_build(ConnectionClass *self) ppacket += sizeof(pversion); for (i = 0; i < cnt; i++) { - strcpy(ppacket, opts[i][0]); - ppacket += (strlen(opts[i][0]) + 1); - strcpy(ppacket, opts[i][1]); - ppacket += (strlen(opts[i][1]) + 1); + strcpy(ppacket, opts[i]); + ppacket += (strlen(opts[i]) + 1); + strcpy(ppacket, vals[i]); + ppacket += (strlen(vals[i]) + 1); } *ppacket = '\0'; @@ -1206,19 +1210,19 @@ static char *protocol3_opts_build(ConnectionClass *self) CSTR func = "protocol3_opts_build"; size_t slen; char *conninfo, *ppacket; - const char *opts[20][2]; + const char *opts[PROTOCOL3_OPTS_MAX], *vals[PROTOCOL3_OPTS_MAX]; int cnt, i; BOOL blankExist; - cnt = protocol3_opts_array(self, opts, TRUE, sizeof(opts) / sizeof(opts[0])); + cnt = protocol3_opts_array(self, opts, vals, TRUE, sizeof(opts) / sizeof(opts[0])); if (cnt < 0) return NULL; slen = sizeof(ProtocolVersion); for (i = 0, slen = 0; i < cnt; i++) { - slen += (strlen(opts[i][0]) + 2 + 2); /* add 2 bytes for safety (literal quotes) */ - slen += strlen(opts[i][1]); + slen += (strlen(opts[i]) + 2 + 2); /* add 2 bytes for safety (literal quotes) */ + slen += strlen(vals[i]); } if (self->login_timeout > 0) { @@ -1240,18 +1244,18 @@ static char *protocol3_opts_build(ConnectionClass *self) for (i = 0, ppacket = conninfo; i < cnt; i++) { - sprintf(ppacket, " %s=", opts[i][0]); - ppacket += (strlen(opts[i][0]) + 2); + sprintf(ppacket, " %s=", opts[i]); + ppacket += (strlen(opts[i]) + 2); blankExist = FALSE; - if (strchr(opts[i][1], ' ')) + if (strchr(vals[i], ' ')) blankExist = TRUE; if (blankExist) { *ppacket = '\''; ppacket++; } - strcpy(ppacket, opts[i][1]); - ppacket += strlen(opts[i][1]); + strcpy(ppacket, vals[i]); + ppacket += strlen(vals[i]); if (blankExist) { *ppacket = '\''; @@ -2372,20 +2376,45 @@ static void CC_clear_cursors(ConnectionClass *self, BOOL on_abort) QResultClass *wres; char cmd[64]; - snprintf(cmd, sizeof(cmd), "MOVE 0 in \"%s\"", QR_get_cursor(res)); - CONNLOCK_RELEASE(self); - wres = CC_send_query(self, cmd, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL); - if (QR_command_maybe_successful(wres)) - QR_set_permanent(res); - else - QR_set_cursor(res, NULL); - QR_Destructor(wres); - CONNLOCK_ACQUIRE(self); + if (QR_needs_survival_check(res)) + { + snprintf(cmd, sizeof(cmd), "MOVE 0 in \"%s\"", QR_get_cursor(res)); + CONNLOCK_RELEASE(self); + wres = CC_send_query(self, cmd, NULL, ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL); + QR_set_no_survival_check(res); + if (QR_command_maybe_successful(wres)) + QR_set_permanent(res); + else + QR_set_cursor(res, NULL); + QR_Destructor(wres); + CONNLOCK_ACQUIRE(self); + } } } } CONNLOCK_RELEASE(self); } + +static void CC_mark_cursors_doubtful(ConnectionClass *self) +{ + int i; + StatementClass *stmt; + QResultClass *res; + + if (!self->ncursors) + return; + CONNLOCK_ACQUIRE(self); + for (i = 0; i < self->num_stmts; i++) + { + stmt = self->stmts[i]; + if (NULL != stmt && + NULL != (res = SC_get_Result(stmt)) && + NULL != QR_get_cursor(res) && + !QR_is_permanent(res)) + QR_set_survival_check(res); + } + CONNLOCK_RELEASE(self); +} void CC_on_commit(ConnectionClass *conn) { @@ -2788,6 +2817,7 @@ inolog("Discarded the first SAVEPOINT\n"); } else if (strnicmp(cmdbuffer, rbkcmd, lenrbkcmd) == 0) { + CC_mark_cursors_doubtful(self); if (PROTOCOL_74(&(self->connInfo))) CC_set_in_error_trans(self); /* mark the transaction error in case of manual rollback */ else @@ -3837,14 +3867,24 @@ inolog("sock=%p\n", sock); } } - if (!(conninfo = protocol3_opts_build(self))) + if (FALSE && connect_with_param_available()) { - if (CC_get_errornumber(self) <= 0) - CC_set_error(self, CONN_OPENDB_ERROR, "Couldn't allcate conninfo", func); - goto cleanup1; + const char *opts[PROTOCOL3_OPTS_MAX], *vals[PROTOCOL3_OPTS_MAX]; + + protocol3_opts_array(self, opts, vals, TRUE, sizeof(opts) / sizeof(opts[0])); + pqconn = CALL_PQconnectdbParams(opts, vals, &libpqLoaded); + } + else + { + if (!(conninfo = protocol3_opts_build(self))) + { + if (CC_get_errornumber(self) <= 0) + CC_set_error(self, CONN_OPENDB_ERROR, "Couldn't allcate conninfo", func); + goto cleanup1; + } + pqconn = CALL_PQconnectdb(conninfo, &libpqLoaded); + free(conninfo); } - pqconn = CALL_PQconnectdb(conninfo, &libpqLoaded); - free(conninfo); if (!libpqLoaded) { CC_set_error(self, CONN_UNABLE_TO_LOAD_DLL, "Couldn't load libpq library", func); diff --git a/convert.c b/convert.c index f4c370b..75ab072 100644 --- a/convert.c +++ b/convert.c @@ -400,9 +400,211 @@ stime2timestamp(const SIMPLE_TIME *st, char *str, BOOL bZone, int precision) return TRUE; } +#if (ODBCVER >= 0x0300) +static +SQLINTERVAL interval2itype(SQLSMALLINT ctype) +{ + SQLINTERVAL sqlitv = 0; + + switch (ctype) + { + case SQL_C_INTERVAL_YEAR: + sqlitv = SQL_IS_YEAR; + break; + case SQL_C_INTERVAL_MONTH: + sqlitv = SQL_IS_MONTH; + break; + case SQL_C_INTERVAL_YEAR_TO_MONTH: + sqlitv = SQL_IS_YEAR_TO_MONTH; + break; + case SQL_C_INTERVAL_DAY: + break; + sqlitv = SQL_IS_DAY; + break; + case SQL_C_INTERVAL_HOUR: + sqlitv = SQL_IS_HOUR; + break; + case SQL_C_INTERVAL_DAY_TO_HOUR: + sqlitv = SQL_IS_DAY_TO_HOUR; + break; + case SQL_C_INTERVAL_MINUTE: + sqlitv = SQL_IS_MINUTE; + break; + case SQL_C_INTERVAL_DAY_TO_MINUTE: + sqlitv = SQL_IS_DAY_TO_MINUTE; + break; + case SQL_C_INTERVAL_HOUR_TO_MINUTE: + sqlitv = SQL_IS_HOUR_TO_MINUTE; + break; + case SQL_C_INTERVAL_SECOND: + sqlitv = SQL_IS_SECOND; + break; + case SQL_C_INTERVAL_DAY_TO_SECOND: + sqlitv = SQL_IS_DAY_TO_SECOND; + break; + case SQL_C_INTERVAL_HOUR_TO_SECOND: + sqlitv = SQL_IS_HOUR_TO_SECOND; + break; + case SQL_C_INTERVAL_MINUTE_TO_SECOND: + sqlitv = SQL_IS_MINUTE_TO_SECOND; + break; + } + return sqlitv; +} + +/* + * Interval data <-----> SQL_INTERVAL_STRUCT + */ + +static int getPrecisionPart(int precision, const char * precPart) +{ + char fraction[] = "000000000"; + int fracs = sizeof(fraction) - 1, cpys; + + if (precision < 0) + precision = 6; /* default */ + if (precision == 0) + return 0; + if ((cpys = strlen(precPart)) > fracs) + cpys = fracs; + memcpy(fraction, precPart, cpys); + fraction[precision] = '\0'; + + return atoi(fraction); +} + +static BOOL +interval2istruct(SQLSMALLINT ctype, int precision, const char *str, SQL_INTERVAL_STRUCT *st) +{ + char lit1[64], lit2[64]; + int scnt, years, mons, days, hours, minutes, seconds; + BOOL sign; + SQLINTERVAL itype = interval2itype(ctype); + + memset(st, sizeof(SQL_INTERVAL_STRUCT), 0); + if ((scnt = sscanf(str, "%d-%d", &years, &mons)) >=2) + { + if (SQL_IS_YEAR_TO_MONTH == itype) + { + sign = years < 0 ? SQL_TRUE : SQL_FALSE; + st->interval_type = itype; + st->interval_sign = sign; + st->intval.year_month.year = sign ? (-years) : years; + st->intval.year_month.month = mons; + return TRUE; + } + return FALSE; + } + else if (scnt = sscanf(str, "%d %02d:%02d:%02d.%09s", &days, &hours, &minutes, &seconds, lit2), 5 == scnt || 4 == scnt) + { + sign = days < 0 ? SQL_TRUE : SQL_FALSE; + st->interval_type = itype; + st->interval_sign = sign; + st->intval.day_second.day = sign ? (-days) : days; + st->intval.day_second.hour = hours; + st->intval.day_second.minute = minutes; + st->intval.day_second.second = seconds; + if (scnt > 4) + st->intval.day_second.fraction = getPrecisionPart(precision, lit2); + return TRUE; + } + else if ((scnt = sscanf(str, "%d %s %d %s", &years, lit1, &mons, lit2)) >=4) + { + if (strnicmp(lit1, "year", 4) == 0 && + strnicmp(lit2, "mon", 2) == 0 && + (SQL_IS_MONTH == itype || + SQL_IS_YEAR_TO_MONTH == itype)) + { + sign = years < 0 ? SQL_TRUE : SQL_FALSE; + st->interval_type = itype; + st->interval_sign = sign; + st->intval.year_month.year = sign ? (-years) : years; + st->intval.year_month.month = sign ? (-mons) : mons; + return TRUE; + } + return FALSE; + } + if ((scnt = sscanf(str, "%d %s %d", &years, lit1, &days)) == 2) + { + sign = years < 0 ? SQL_TRUE : SQL_FALSE; + if (SQL_IS_YEAR == itype && + (stricmp(lit1, "year") == 0 || + stricmp(lit1, "years") == 0)) + { + st->interval_type = itype; + st->interval_sign = sign; + st->intval.year_month.year = sign ? (-years) : years; + return TRUE; + } + if (SQL_IS_MONTH == itype && + (stricmp(lit1, "mon") == 0 || + stricmp(lit1, "mons") == 0)) + { + st->interval_type = itype; + st->interval_sign = sign; + st->intval.year_month.month = sign ? (-years) : years; + return TRUE; + } + if (SQL_IS_DAY == itype && + (stricmp(lit1, "day") == 0 || + stricmp(lit1, "days") == 0)) + { + st->interval_type = itype; + st->interval_sign = sign; + st->intval.day_second.day = sign ? (-years) : years; + return TRUE; + } + return FALSE; + } + switch (itype) + { + case SQL_IS_YEAR: + case SQL_IS_MONTH: + case SQL_IS_YEAR_TO_MONTH: + return FALSE; + } + scnt = sscanf(str, "%d %s %02d:%02d:%02d.%09s", &days, lit1, &hours, &minutes, &seconds, lit2); + if (strnicmp(lit1, "day", 3) != 0) + return FALSE; + sign = days < 0 ? SQL_TRUE : SQL_FALSE; + switch (scnt) + { + case 5: + case 6: + st->interval_type = itype; + st->interval_sign = sign; + st->intval.day_second.day = sign ? (-days) : days; + st->intval.day_second.hour = sign ? (-hours) : hours; + st->intval.day_second.minute = minutes; + st->intval.day_second.second = seconds; + if (scnt > 5) + st->intval.day_second.fraction = getPrecisionPart(precision, lit2); + return TRUE; + } + scnt = sscanf(str, "%02d:%02d:%02d.%09s", &hours, &minutes, &seconds, lit2); + sign = hours < 0 ? SQL_TRUE : SQL_FALSE; + switch (scnt) + { + case 3: + case 4: + st->interval_type = itype; + st->interval_sign = sign; + st->intval.day_second.hour = sign ? (-hours) : hours; + st->intval.day_second.minute = minutes; + st->intval.day_second.second = seconds; + if (scnt > 3) + st->intval.day_second.fraction = getPrecisionPart(precision, lit2); + return TRUE; + } + + return FALSE; +} +#endif /* ODBCVER */ + + /* This is called by SQLFetch() */ int -copy_and_convert_field_bindinfo(StatementClass *stmt, OID field_type, void *value, int col) +copy_and_convert_field_bindinfo(StatementClass *stmt, OID field_type, int atttypmod, void *value, int col) { ARDFields *opts = SC_get_ARDF(stmt); BindInfoClass *bic; @@ -412,9 +614,10 @@ copy_and_convert_field_bindinfo(StatementClass *stmt, OID field_type, void *valu extend_column_bindings(opts, col + 1); bic = &(opts->bindings[col]); SC_set_current_col(stmt, -1); - return copy_and_convert_field(stmt, field_type, value, bic->returntype, - (PTR) (bic->buffer + offset), bic->buflen, - LENADDR_SHIFT(bic->used, offset), LENADDR_SHIFT(bic->indicator, offset)); + return copy_and_convert_field(stmt, field_type, atttypmod, value, + bic->returntype, bic->precision, + (PTR) (bic->buffer + offset), bic->buflen, + LENADDR_SHIFT(bic->used, offset), LENADDR_SHIFT(bic->indicator, offset)); } static double get_double_value(const char *str) @@ -459,9 +662,12 @@ static int char2guid(const char *str, SQLGUID *g) /* This is called by SQLGetData() */ int -copy_and_convert_field(StatementClass *stmt, OID field_type, void *valuei, - SQLSMALLINT fCType, PTR rgbValue, SQLLEN cbValueMax, - SQLLEN *pcbValue, SQLLEN *pIndicator) +copy_and_convert_field(StatementClass *stmt, + OID field_type, int atttypmod, + void *valuei, + SQLSMALLINT fCType, int precision, + PTR rgbValue, SQLLEN cbValueMax, + SQLLEN *pcbValue, SQLLEN *pIndicator) { CSTR func = "copy_and_convert_field"; const char *value = valuei; @@ -483,8 +689,8 @@ copy_and_convert_field(StatementClass *stmt, OID field_type, void *valuei, SQLSETPOSIROW 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 ConnectionClass *conn = SC_get_conn(stmt); + BOOL changed; BOOL text_handling, localize_needed; const char *neut_str = value; char midtemp[2][32]; @@ -721,6 +927,7 @@ inolog("2stime fr=%d\n", std_time.fr); case PG_TYPE_BOOL: { /* change T/F to 1/0 */ char *s; + ConnInfo *ci = &(conn->connInfo); s = midtemp[mtemp_cnt]; switch (((char *)value)[0]) @@ -733,7 +940,7 @@ inolog("2stime fr=%d\n", std_time.fr); strcpy(s, "0"); break; default: - if (true_is_minus1) + if (ci->true_is_minus1) strcpy(s, "-1"); else strcpy(s, "1"); @@ -817,7 +1024,7 @@ inolog("2stime fr=%d\n", std_time.fr); /* Change default into something useable */ if (fCType == SQL_C_DEFAULT) { - fCType = pgtype_to_ctype(stmt, field_type); + fCType = pgtype_attr_to_ctype(conn, field_type, atttypmod); if (fCType == SQL_C_WCHAR && CC_default_is_c(conn)) fCType = SQL_C_CHAR; @@ -1609,6 +1816,22 @@ inolog("SQL_C_VARBOOKMARK value=%d\n", ival); *((SQLGUID *) rgbValue + bind_row) = g; break; #endif /* ODBCVER */ +#if (ODBCVER >= 0x0300) + case SQL_C_INTERVAL_YEAR: + case SQL_C_INTERVAL_MONTH: + case SQL_C_INTERVAL_YEAR_TO_MONTH: + case SQL_C_INTERVAL_DAY: + case SQL_C_INTERVAL_HOUR: + case SQL_C_INTERVAL_DAY_TO_HOUR: + case SQL_C_INTERVAL_MINUTE: + case SQL_C_INTERVAL_HOUR_TO_MINUTE: + case SQL_C_INTERVAL_SECOND: + case SQL_C_INTERVAL_DAY_TO_SECOND: + case SQL_C_INTERVAL_HOUR_TO_SECOND: + case SQL_C_INTERVAL_MINUTE_TO_SECOND: + interval2istruct(fCType, precision, neut_str, bind_size > 0 ? (SQL_INTERVAL_STRUCT *) rgbValueBindRow : (SQL_INTERVAL_STRUCT *) rgbValue + bind_row); + break; +#endif /* ODBCVER */ default: qlog("conversion to the type %d isn't supported\n", fCType); @@ -2364,7 +2587,7 @@ Prepare_and_convert(StatementClass *stmt, QueryParse *qp, QueryBuild *qb) if (outpara) CVT_APPEND_STR(qb, "void"); else - CVT_APPEND_STR(qb, pgtype_to_name(stmt, PIC_dsp_pgtype(stmt, ipdopts->parameters[i]), FALSE)); + CVT_APPEND_STR(qb, pgtype_to_name(stmt, PIC_dsp_pgtype(conn, ipdopts->parameters[i]), -1, FALSE)); oc++; } CVT_APPEND_CHAR(qb, ')'); @@ -3390,11 +3613,11 @@ inolog("num_p=%d\n", num_p); memset(bindreq + leng, 0, sizeof(Int2) * num_p); /* initialize by text format */ for (i = stmt->proc_return, j = 0; i < num_params; i++) { -inolog("%dth parameter type oid is %u\n", i, PIC_dsp_pgtype(stmt, parameters[i])); +inolog("%dth parameter type oid is %u\n", i, PIC_dsp_pgtype(conn, parameters[i])); if (discard_output && SQL_PARAM_OUTPUT == parameters[i].paramType) continue; - if (PG_TYPE_BYTEA == PIC_dsp_pgtype(stmt, parameters[i])) + if (PG_TYPE_BYTEA == PIC_dsp_pgtype(conn, parameters[i])) { mylog("%dth parameter is of binary format\n", j); memcpy(bindreq + leng + sizeof(Int2) * j, @@ -3630,6 +3853,10 @@ ResolveOneParam(QueryBuild *qb, QueryParse *qp) BOOL outputDiscard, valueOutput; SDOUBLE dbv; SFLOAT flv; +#if (ODBCVER >= 0x0300) + SQL_INTERVAL_STRUCT *ivstruct; + const char *ivsign; +#endif /* ODBCVER */ outputDiscard = (0 != (qb->flags & FLGB_DISCARD_OUTPUT)); valueOutput = (0 == (qb->flags & (FLGB_PRE_EXECUTING | FLGB_BUILDING_PREPARE_STATEMENT))); @@ -3650,6 +3877,7 @@ inolog("resolveOneParam %d(%d,%d)\n", param_number, ipdopts->allocated, apdopts- ipara = ipdopts->parameters + param_number; if ((!apara || !ipara) && valueOutput) { +mylog("!!! The # of binded parameters (%d, %d) < the # of parameter markers %d\n", apdopts->allocated, ipdopts->allocated, param_number); qb->errormsg = "The # of binded parameters < the # of parameter markers"; qb->errornumber = STMT_COUNT_FIELD_INCORRECT; CVT_TERMINATE(qb); /* just in case */ @@ -3843,7 +4071,7 @@ inolog("ipara=%p paramType=%d %d proc_return=%d\n", ipara, ipara ? ipara->paramT param_ctype = apara->CType; param_sqltype = ipara->SQLType; - param_pgtype = PIC_dsp_pgtype(qb->stmt, *ipara); + param_pgtype = PIC_dsp_pgtype(qb->conn, *ipara); mylog("%s: from(fcType)=%d, to(fSqlType)=%d(%u)\n", func, param_ctype, param_sqltype, param_pgtype); @@ -3871,6 +4099,9 @@ inolog("ipara=%p paramType=%d %d proc_return=%d\n", ipara, ipara ? ipara->paramT st.d = tim->tm_mday; st.y = tim->tm_year + 1900; +#if (ODBCVER >= 0x0300) + ivstruct = (SQL_INTERVAL_STRUCT *) buffer; +#endif /* ODBCVER */ /* Convert input C type to a neutral format */ switch (param_ctype) { @@ -4037,6 +4268,49 @@ mylog("C_WCHAR=%s(%d)\n", buffer, used); case SQL_C_NUMERIC: if (ResolveNumericParam((SQL_NUMERIC_STRUCT *) buffer, param_string)) break; + case SQL_C_INTERVAL_YEAR: + ivsign = ivstruct->interval_sign ? "-" : ""; + sprintf(param_string, "%s%d years", ivsign, ivstruct->intval.year_month.year); + break; + case SQL_C_INTERVAL_MONTH: + case SQL_C_INTERVAL_YEAR_TO_MONTH: + ivsign = ivstruct->interval_sign ? "-" : ""; + sprintf(param_string, "%s%d years %s%d mons", ivsign, ivstruct->intval.year_month.year, ivsign, ivstruct->intval.year_month.month); + break; + case SQL_C_INTERVAL_DAY: + ivsign = ivstruct->interval_sign ? "-" : ""; + sprintf(param_string, "%s%d days", ivsign, ivstruct->intval.day_second.day); + break; + case SQL_C_INTERVAL_HOUR: + case SQL_C_INTERVAL_DAY_TO_HOUR: + ivsign = ivstruct->interval_sign ? "-" : ""; + sprintf(param_string, "%s%d days %s%02d:00:00", ivsign, ivstruct->intval.day_second.day, ivsign, ivstruct->intval.day_second.hour); + break; + case SQL_C_INTERVAL_MINUTE: + case SQL_C_INTERVAL_HOUR_TO_MINUTE: + case SQL_C_INTERVAL_DAY_TO_MINUTE: + ivsign = ivstruct->interval_sign ? "-" : ""; + sprintf(param_string, "%s%d days %s%02d:%02d:00", ivsign, ivstruct->intval.day_second.day, ivsign, ivstruct->intval.day_second.hour, ivstruct->intval.day_second.minute); + break; + + case SQL_C_INTERVAL_SECOND: + case SQL_C_INTERVAL_DAY_TO_SECOND: + case SQL_C_INTERVAL_HOUR_TO_SECOND: + case SQL_C_INTERVAL_MINUTE_TO_SECOND: + ivsign = ivstruct->interval_sign ? "-" : ""; + sprintf(param_string, "%s%d days %s%02d:%02d:%02%d", ivsign, ivstruct->intval.day_second.day, ivsign, ivstruct->intval.day_second.hour, ivstruct->intval.day_second.minute, ivstruct->intval.day_second.second); + if (ivstruct->intval.day_second.fraction > 0) + { + int fraction = ivstruct->intval.day_second.fraction, prec = apara->precision; + + while (fraction % 10 == 0) + { + fraction /= 10; + prec--; + } + sprintf(¶m_string[strlen(param_string)], ".%0*d", prec, fraction); + } + break; #endif #if (ODBCVER >= 0x0350) case SQL_C_GUID: diff --git a/convert.h b/convert.h index 6e2685a..166763c 100644 --- a/convert.h +++ b/convert.h @@ -38,10 +38,12 @@ typedef struct int fr; } SIMPLE_TIME; -int copy_and_convert_field_bindinfo(StatementClass *stmt, OID field_type, void *value, int col); -int copy_and_convert_field(StatementClass *stmt, OID field_type, - void *value, SQLSMALLINT fCType, PTR rgbValue, - SQLLEN cbValueMax, SQLLEN *pcbValue, SQLLEN *pIndicator); +int copy_and_convert_field_bindinfo(StatementClass *stmt, OID field_type, int atttypmod, void *value, int col); +int copy_and_convert_field(StatementClass *stmt, + OID field_type, int atttypmod, + void *value, + SQLSMALLINT fCType, int precision, + PTR rgbValue, SQLLEN cbValueMax, SQLLEN *pcbValue, SQLLEN *pIndicator); int copy_statement_with_parameters(StatementClass *stmt, BOOL); BOOL convert_money(const char *s, char *sout, size_t soutmax); diff --git a/dlg_specific.h b/dlg_specific.h index 033b7f7..34beaaf 100644 --- a/dlg_specific.h +++ b/dlg_specific.h @@ -25,6 +25,7 @@ extern "C" { #define UNKNOWNS_AS_MAX 0 #define UNKNOWNS_AS_DONTKNOW 1 #define UNKNOWNS_AS_LONGEST 2 +#define UNKNOWNS_AS_CATALOG 100 /* ODBC initialization files */ #ifndef WIN32 diff --git a/environ.c b/environ.c index 651663e..87e25d0 100644 --- a/environ.c +++ b/environ.c @@ -35,11 +35,22 @@ static ConnectionClass **conns = NULL; #if defined(WIN_MULTITHREAD_SUPPORT) CRITICAL_SECTION conns_cs; CRITICAL_SECTION common_cs; /* commonly used for short term blocking */ +CRITICAL_SECTION common_lcs; /* commonly used for not necessarily short term blocking */ #elif defined(POSIX_MULTITHREAD_SUPPORT) pthread_mutex_t conns_cs; pthread_mutex_t common_cs; +pthread_mutex_t common_lcs; #endif /* WIN_MULTITHREAD_SUPPORT */ +void shortterm_common_lock() +{ + ENTER_COMMON_CS; +} +void shortterm_common_unlock() +{ + LEAVE_COMMON_CS; +} + int getConnCount() { return conns_count; diff --git a/environ.h b/environ.h index 24fe6ae..384a380 100644 --- a/environ.h +++ b/environ.h @@ -96,6 +96,8 @@ ConnectionClass * const *getConnList(); #define DELETE_COMMON_CS #endif /* WIN_MULTITHREAD_SUPPORT */ +void shortterm_common_lock(); +void shortterm_common_unlock(); #ifdef __cplusplus } #endif diff --git a/execute.c b/execute.c index 517c307..750edf1 100644 --- a/execute.c +++ b/execute.c @@ -1646,7 +1646,7 @@ PGAPI_PutData( putlen = ctype_length(ctype); } putbuf = rgbValue; - handling_lo = (PIC_dsp_pgtype(stmt, *current_iparam) == conn->lobj_type); + handling_lo = (PIC_dsp_pgtype(conn, *current_iparam) == conn->lobj_type); if (handling_lo && SQL_C_CHAR == ctype) { allocbuf = malloc(putlen / 2 + 1); diff --git a/info.c b/info.c index 346a499..a462513 100644 --- a/info.c +++ b/info.c @@ -40,7 +40,6 @@ #include "multibyte.h" #include "catfunc.h" - /* Trigger related stuff for SQLForeign Keys */ #define TRIGGER_SHIFT 3 #define TRIGGER_MASK 0x03 @@ -894,7 +893,7 @@ PGAPI_GetTypeInfo( for (i = 0, sqlType = sqlTypes[0]; sqlType; sqlType = sqlTypes[++i]) { - pgType = sqltype_to_pgtype(stmt, sqlType); + pgType = sqltype_to_pgtype(conn, sqlType); if (sqlType == SQL_LONGVARBINARY) { @@ -929,19 +928,19 @@ mylog("aunq_match=%d pgtcount=%d\n", aunq_match, pgtcount); /* These values can't be NULL */ if (aunq_match == cnt) { - set_tuplefield_string(&tuple[0], pgtype_to_name(stmt, pgType, TRUE)); + set_tuplefield_string(&tuple[0], pgtype_to_name(stmt, pgType, PG_UNSPECIFIED, TRUE)); set_tuplefield_int2(&tuple[6], SQL_NO_NULLS); inolog("serial in\n"); } else { - set_tuplefield_string(&tuple[0], pgtype_to_name(stmt, pgType, FALSE)); - set_tuplefield_int2(&tuple[6], pgtype_nullable(stmt, pgType)); + set_tuplefield_string(&tuple[0], pgtype_to_name(stmt, pgType, PG_UNSPECIFIED, FALSE)); + set_tuplefield_int2(&tuple[6], pgtype_nullable(conn, pgType)); } set_tuplefield_int2(&tuple[1], (Int2) sqlType); - set_tuplefield_int2(&tuple[7], pgtype_case_sensitive(stmt, pgType)); - set_tuplefield_int2(&tuple[8], pgtype_searchable(stmt, pgType)); - set_tuplefield_int2(&tuple[10], pgtype_money(stmt, pgType)); + set_tuplefield_int2(&tuple[7], pgtype_case_sensitive(conn, pgType)); + set_tuplefield_int2(&tuple[8], pgtype_searchable(conn, pgType)); + set_tuplefield_int2(&tuple[10], pgtype_money(conn, pgType)); /* * Localized data-source dependent data type name (always @@ -951,23 +950,23 @@ inolog("serial in\n"); /* These values can be NULL */ set_nullfield_int4(&tuple[2], pgtype_column_size(stmt, pgType, PG_STATIC, UNKNOWNS_AS_DEFAULT)); - set_nullfield_string(&tuple[3], pgtype_literal_prefix(stmt, pgType)); - set_nullfield_string(&tuple[4], pgtype_literal_suffix(stmt, pgType)); - set_nullfield_string(&tuple[5], pgtype_create_params(stmt, pgType)); + set_nullfield_string(&tuple[3], pgtype_literal_prefix(conn, pgType)); + set_nullfield_string(&tuple[4], pgtype_literal_suffix(conn, pgType)); + set_nullfield_string(&tuple[5], pgtype_create_params(conn, pgType)); if (1 < pgtcount) set_tuplefield_int2(&tuple[9], SQL_TRUE); else - set_nullfield_int2(&tuple[9], pgtype_unsigned(stmt, pgType)); + set_nullfield_int2(&tuple[9], pgtype_unsigned(conn, pgType)); if (aunq_match == cnt) set_tuplefield_int2(&tuple[11], SQL_TRUE); else - set_nullfield_int2(&tuple[11], pgtype_auto_increment(stmt, pgType)); - set_nullfield_int2(&tuple[13], pgtype_min_decimal_digits(stmt, pgType)); - set_nullfield_int2(&tuple[14], pgtype_max_decimal_digits(stmt, pgType)); + set_nullfield_int2(&tuple[11], pgtype_auto_increment(conn, pgType)); + set_nullfield_int2(&tuple[13], pgtype_min_decimal_digits(conn, pgType)); + set_nullfield_int2(&tuple[14], pgtype_max_decimal_digits(conn, pgType)); #if (ODBCVER >=0x0300) set_nullfield_int2(&tuple[15], pgtype_to_sqldesctype(stmt, pgType, PG_STATIC)); - set_nullfield_int2(&tuple[16], pgtype_to_datetime_sub(stmt, pgType)); - set_nullfield_int4(&tuple[17], pgtype_radix(stmt, pgType)); + set_nullfield_int2(&tuple[16], pgtype_to_datetime_sub(stmt, pgType, PG_UNSPECIFIED)); + set_nullfield_int4(&tuple[17], pgtype_radix(conn, pgType)); set_nullfield_int4(&tuple[18], 0); #endif /* ODBCVER */ } @@ -2009,15 +2008,16 @@ PGAPI_Columns( field_name[MAX_INFO_STRING], field_type_name[MAX_INFO_STRING]; Int2 field_number, sqltype, concise_type, - result_cols, - decimal_digits; - Int4 field_length, - mod_length, - column_size, + result_cols; + Int4 mod_length, ordinal, typmod; OID field_type, the_type, greloid, basetype; +#ifdef USE_OLD_IMPL + Int2 decimal_digits; + Int4 field_length, column_size; char useStaticPrecision, useStaticScale; +#endif /* USE_OLD_IMPL */ char not_null[MAX_INFO_STRING], relhasrules[MAX_INFO_STRING], relkind[8]; char *escSchemaName = NULL, *escTableName = NULL, *escColumnName = NULL; @@ -2305,7 +2305,11 @@ retry_public_schema: result_cols = NUM_OF_COLUMNS_FIELDS; extend_column_bindings(SC_get_ARDF(stmt), result_cols); - stmt->catalog_result = TRUE; + /* + * Setting catalog_result here affects the behavior of + * pgtype_xxx() functions. So set it later. + * stmt->catalog_result = TRUE; + */ /* set the field names */ QR_set_num_fields(res, result_cols); QR_set_field_info_v(res, COLUMNS_CATALOG_NAME, "TABLE_QUALIFIER", PG_TYPE_VARCHAR, MAX_INFO_STRING); @@ -2374,7 +2378,7 @@ retry_public_schema: set_tuplefield_int4(&tuple[COLUMNS_PRECISION], pgtype_column_size(stmt, the_type, PG_STATIC, UNKNOWNS_AS_DEFAULT)); set_tuplefield_int4(&tuple[COLUMNS_LENGTH], pgtype_buffer_length(stmt, the_type, PG_STATIC, UNKNOWNS_AS_DEFAULT)); set_nullfield_int2(&tuple[COLUMNS_SCALE], pgtype_decimal_digits(stmt, the_type, PG_STATIC)); - set_nullfield_int2(&tuple[COLUMNS_RADIX], pgtype_radix(stmt, the_type)); + set_nullfield_int2(&tuple[COLUMNS_RADIX], pgtype_radix(conn, the_type)); set_tuplefield_int2(&tuple[COLUMNS_NULLABLE], SQL_NO_NULLS); set_tuplefield_string(&tuple[COLUMNS_REMARKS], NULL_STRING); @@ -2464,6 +2468,7 @@ mylog(" and the data=%s\n", attdef); qlog("%s: table='%s',field_name='%s',type=%d,name='%s'\n", func, table_name, field_name, field_type, field_type_name); +#ifdef USE_OLD_IMPL useStaticPrecision = TRUE; useStaticScale = TRUE; @@ -2535,7 +2540,7 @@ mylog(" and the data=%s\n", attdef); #endif /* UNICODE_SUPPORT */ set_tuplefield_int4(&tuple[COLUMNS_LENGTH], field_length); #if (ODBCVER >= 0x0300) - set_tuplefield_int4(&tuple[COLUMNS_CHAR_OCTET_LENGTH], pgtype_transfer_octet_length(stmt, field_type, mod_length)); + set_tuplefield_int4(&tuple[COLUMNS_CHAR_OCTET_LENGTH], pgtype_transfer_octet_length(conn, field_type, mod_length)); #endif /* ODBCVER */ set_tuplefield_int4(&tuple[COLUMNS_DISPLAY_SIZE], mod_length); } @@ -2558,15 +2563,36 @@ mylog(" and the data=%s\n", attdef); if (SQL_TYPE_NULL == sqltype) { - sqltype = pgtype_to_concise_type(stmt, field_type, PG_STATIC); - concise_type = pgtype_to_sqldesctype(stmt, field_type, PG_STATIC); + sqltype = pgtype_attr_to_concise_type(conn, field_type, mod_length, -1); + concise_type = pgtype_attr_to_sqldesctype(conn, field_type, mod_length); } else concise_type = sqltype; +#else /* USE_OLD_IMPL */ + /* Subtract the header length */ + switch (field_type) + { + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP_NO_TMZONE: + case PG_TYPE_TIME: + case PG_TYPE_TIME_WITH_TMZONE: + break; + default: + mod_length -= 4; + } + set_tuplefield_int4(&tuple[COLUMNS_PRECISION], pgtype_attr_column_size(conn, field_type, mod_length, PG_UNSPECIFIED, UNKNOWNS_AS_DEFAULT)); + set_tuplefield_int4(&tuple[COLUMNS_LENGTH], pgtype_attr_buffer_length(conn, field_type, mod_length, PG_UNSPECIFIED, UNKNOWNS_AS_DEFAULT)); + set_tuplefield_int4(&tuple[COLUMNS_DISPLAY_SIZE], pgtype_attr_display_size(conn, field_type, mod_length, PG_UNSPECIFIED, UNKNOWNS_AS_DEFAULT)); + set_nullfield_int2(&tuple[COLUMNS_SCALE], pgtype_attr_decimal_digits(conn, field_type, mod_length, PG_UNSPECIFIED, UNKNOWNS_AS_DEFAULT)); + + sqltype = pgtype_attr_to_concise_type(conn, field_type, mod_length, PG_UNSPECIFIED); + concise_type = pgtype_attr_to_sqldesctype(conn, field_type, mod_length); +#endif /* USE_OLD_IMPL */ + set_tuplefield_int2(&tuple[COLUMNS_DATA_TYPE], sqltype); - set_nullfield_int2(&tuple[COLUMNS_RADIX], pgtype_radix(stmt, field_type)); - set_tuplefield_int2(&tuple[COLUMNS_NULLABLE], (Int2) (not_null[0] == '1' ? SQL_NO_NULLS : pgtype_nullable(stmt, field_type))); + set_nullfield_int2(&tuple[COLUMNS_RADIX], pgtype_radix(conn, field_type)); + set_tuplefield_int2(&tuple[COLUMNS_NULLABLE], (Int2) (not_null[0] == '1' ? SQL_NO_NULLS : pgtype_nullable(conn, field_type))); set_tuplefield_string(&tuple[COLUMNS_REMARKS], NULL_STRING); #if (ODBCVER >= 0x0300) if (attdef && strlen(attdef) > INFO_VARCHAR_SIZE) @@ -2574,7 +2600,8 @@ mylog(" and the data=%s\n", attdef); else set_tuplefield_string(&tuple[COLUMNS_COLUMN_DEF], attdef); set_tuplefield_int2(&tuple[COLUMNS_SQL_DATA_TYPE], concise_type); - set_nullfield_int2(&tuple[COLUMNS_SQL_DATETIME_SUB], pgtype_to_datetime_sub(stmt, field_type)); + set_nullfield_int2(&tuple[COLUMNS_SQL_DATETIME_SUB], pgtype_attr_to_datetime_sub(conn, field_type, mod_length)); + set_tuplefield_int4(&tuple[COLUMNS_CHAR_OCTET_LENGTH], pgtype_attr_transfer_octet_length(conn, field_type, mod_length, UNKNOWNS_AS_DEFAULT)); set_tuplefield_int4(&tuple[COLUMNS_ORDINAL_POSITION], ordinal); set_tuplefield_null(&tuple[COLUMNS_IS_NULLABLE]); #endif /* ODBCVER */ @@ -2615,11 +2642,11 @@ mylog(" and the data=%s\n", attdef); set_tuplefield_string(&tuple[COLUMNS_COLUMN_NAME], "xmin"); sqltype = pgtype_to_concise_type(stmt, the_type, PG_STATIC); set_tuplefield_int2(&tuple[COLUMNS_DATA_TYPE], sqltype); - set_tuplefield_string(&tuple[COLUMNS_TYPE_NAME], pgtype_to_name(stmt, the_type, FALSE)); + set_tuplefield_string(&tuple[COLUMNS_TYPE_NAME], pgtype_to_name(stmt, the_type, PG_UNSPECIFIED, FALSE)); set_tuplefield_int4(&tuple[COLUMNS_PRECISION], pgtype_column_size(stmt, the_type, PG_STATIC, UNKNOWNS_AS_DEFAULT)); set_tuplefield_int4(&tuple[COLUMNS_LENGTH], pgtype_buffer_length(stmt, the_type, PG_STATIC, UNKNOWNS_AS_DEFAULT)); set_nullfield_int2(&tuple[COLUMNS_SCALE], pgtype_decimal_digits(stmt, the_type, PG_STATIC)); - set_nullfield_int2(&tuple[COLUMNS_RADIX], pgtype_radix(stmt, the_type)); + set_nullfield_int2(&tuple[COLUMNS_RADIX], pgtype_radix(conn, the_type)); set_tuplefield_int2(&tuple[COLUMNS_NULLABLE], SQL_NO_NULLS); set_tuplefield_string(&tuple[COLUMNS_REMARKS], NULL_STRING); #if (ODBCVER >= 0x0300) @@ -2647,6 +2674,7 @@ cleanup: * results can be retrieved. */ stmt->status = STMT_FINISHED; + stmt->catalog_result = TRUE; /* set up the current tuple pointer for SQLFetch */ stmt->currTuple = -1; @@ -2852,7 +2880,7 @@ retry_public_schema: set_tuplefield_null(&tuple[0]); set_tuplefield_string(&tuple[1], "ctid"); set_tuplefield_int2(&tuple[2], pgtype_to_concise_type(stmt, the_type, PG_STATIC)); - set_tuplefield_string(&tuple[3], pgtype_to_name(stmt, the_type, FALSE)); + set_tuplefield_string(&tuple[3], pgtype_to_name(stmt, the_type, PG_UNSPECIFIED, FALSE)); set_tuplefield_int4(&tuple[4], pgtype_column_size(stmt, the_type, PG_STATIC, UNKNOWNS_AS_DEFAULT)); set_tuplefield_int4(&tuple[5], pgtype_buffer_length(stmt, the_type, PG_STATIC, UNKNOWNS_AS_DEFAULT)); set_tuplefield_int2(&tuple[6], pgtype_decimal_digits(stmt, the_type, PG_STATIC)); @@ -2876,7 +2904,7 @@ inolog("Add ctid\n"); set_tuplefield_int2(&tuple[0], SQL_SCOPE_SESSION); set_tuplefield_string(&tuple[1], OID_NAME); set_tuplefield_int2(&tuple[2], pgtype_to_concise_type(stmt, the_type, PG_STATIC)); - set_tuplefield_string(&tuple[3], pgtype_to_name(stmt, the_type, TRUE)); + set_tuplefield_string(&tuple[3], pgtype_to_name(stmt, the_type, PG_UNSPECIFIED, TRUE)); set_tuplefield_int4(&tuple[4], pgtype_column_size(stmt, the_type, PG_STATIC, UNKNOWNS_AS_DEFAULT)); set_tuplefield_int4(&tuple[5], pgtype_buffer_length(stmt, the_type, PG_STATIC, UNKNOWNS_AS_DEFAULT)); set_tuplefield_int2(&tuple[6], pgtype_decimal_digits(stmt, the_type, PG_STATIC)); @@ -2893,7 +2921,7 @@ inolog("Add ctid\n"); set_tuplefield_null(&tuple[0]); set_tuplefield_string(&tuple[1], "xmin"); set_tuplefield_int2(&tuple[2], pgtype_to_concise_type(stmt, the_type, PG_STATIC)); - set_tuplefield_string(&tuple[3], pgtype_to_name(stmt, the_type, FALSE)); + set_tuplefield_string(&tuple[3], pgtype_to_name(stmt, the_type, PG_UNSPECIFIED, FALSE)); set_tuplefield_int4(&tuple[4], pgtype_column_size(stmt, the_type, PG_STATIC, UNKNOWNS_AS_DEFAULT)); set_tuplefield_int4(&tuple[5], pgtype_buffer_length(stmt, the_type, PG_STATIC, UNKNOWNS_AS_DEFAULT)); set_tuplefield_int2(&tuple[6], pgtype_decimal_digits(stmt, the_type, PG_STATIC)); @@ -5108,19 +5136,19 @@ mylog("atttypid=%s\n", atttypid ? atttypid : "(null)"); set_tuplefield_string(&tuple[PROCOLS_COLUMN_NAME], NULL_STRING); set_tuplefield_int2(&tuple[PROCOLS_COLUMN_TYPE], SQL_RETURN_VALUE); set_tuplefield_int2(&tuple[PROCOLS_DATA_TYPE], pgtype_to_concise_type(stmt, pgtype, PG_STATIC)); - set_tuplefield_string(&tuple[PROCOLS_TYPE_NAME], pgtype_to_name(stmt, pgtype, FALSE)); + set_tuplefield_string(&tuple[PROCOLS_TYPE_NAME], pgtype_to_name(stmt, pgtype, PG_UNSPECIFIED, FALSE)); column_size = pgtype_column_size(stmt, pgtype, PG_STATIC, UNKNOWNS_AS_DEFAULT); set_nullfield_int4(&tuple[PROCOLS_COLUMN_SIZE], column_size); set_tuplefield_int4(&tuple[PROCOLS_BUFFER_LENGTH], pgtype_buffer_length(stmt, pgtype, PG_STATIC, UNKNOWNS_AS_DEFAULT)); set_nullfield_int2(&tuple[PROCOLS_DECIMAL_DIGITS], pgtype_decimal_digits(stmt, pgtype, PG_STATIC)); - set_nullfield_int2(&tuple[PROCOLS_NUM_PREC_RADIX], pgtype_radix(stmt, pgtype)); + set_nullfield_int2(&tuple[PROCOLS_NUM_PREC_RADIX], pgtype_radix(conn, pgtype)); set_tuplefield_int2(&tuple[PROCOLS_NULLABLE], SQL_NULLABLE_UNKNOWN); set_tuplefield_null(&tuple[PROCOLS_REMARKS]); #if (ODBCVER >= 0x0300) set_tuplefield_null(&tuple[PROCOLS_COLUMN_DEF]); set_nullfield_int2(&tuple[PROCOLS_SQL_DATA_TYPE], pgtype_to_sqldesctype(stmt, pgtype, PG_STATIC)); - set_nullfield_int2(&tuple[PROCOLS_SQL_DATETIME_SUB], pgtype_to_datetime_sub(stmt, pgtype)); - set_nullfield_int4(&tuple[PROCOLS_CHAR_OCTET_LENGTH], pgtype_transfer_octet_length(stmt, pgtype, column_size)); + set_nullfield_int2(&tuple[PROCOLS_SQL_DATETIME_SUB], pgtype_to_datetime_sub(stmt, pgtype, PG_UNSPECIFIED)); + set_nullfield_int4(&tuple[PROCOLS_CHAR_OCTET_LENGTH], pgtype_attr_transfer_octet_length(conn, pgtype, PG_UNSPECIFIED, UNKNOWNS_AS_DEFAULT)); set_tuplefield_int4(&tuple[PROCOLS_ORDINAL_POSITION], 0); set_tuplefield_string(&tuple[PROCOLS_IS_NULLABLE], NULL_STRING); #endif /* ODBCVER >= 0x0300 */ @@ -5234,19 +5262,19 @@ mylog("atttypid=%s\n", atttypid ? atttypid : "(null)"); else set_tuplefield_int2(&tuple[PROCOLS_COLUMN_TYPE], SQL_PARAM_INPUT); set_tuplefield_int2(&tuple[PROCOLS_DATA_TYPE], pgtype_to_concise_type(stmt, pgtype, PG_STATIC)); - set_tuplefield_string(&tuple[PROCOLS_TYPE_NAME], pgtype_to_name(stmt, pgtype, FALSE)); + set_tuplefield_string(&tuple[PROCOLS_TYPE_NAME], pgtype_to_name(stmt, pgtype, PG_UNSPECIFIED, FALSE)); column_size = pgtype_column_size(stmt, pgtype, PG_STATIC, UNKNOWNS_AS_DEFAULT); set_nullfield_int4(&tuple[PROCOLS_COLUMN_SIZE], column_size); set_tuplefield_int4(&tuple[PROCOLS_BUFFER_LENGTH], pgtype_buffer_length(stmt, pgtype, PG_STATIC, UNKNOWNS_AS_DEFAULT)); set_nullfield_int2(&tuple[PROCOLS_DECIMAL_DIGITS], pgtype_decimal_digits(stmt, pgtype, PG_STATIC)); - set_nullfield_int2(&tuple[PROCOLS_NUM_PREC_RADIX], pgtype_radix(stmt, pgtype)); + set_nullfield_int2(&tuple[PROCOLS_NUM_PREC_RADIX], pgtype_radix(conn, pgtype)); set_tuplefield_int2(&tuple[PROCOLS_NULLABLE], SQL_NULLABLE_UNKNOWN); set_tuplefield_null(&tuple[PROCOLS_REMARKS]); #if (ODBCVER >= 0x0300) set_tuplefield_null(&tuple[PROCOLS_COLUMN_DEF]); set_nullfield_int2(&tuple[PROCOLS_SQL_DATA_TYPE], pgtype_to_sqldesctype(stmt, pgtype, PG_STATIC)); - set_nullfield_int2(&tuple[PROCOLS_SQL_DATETIME_SUB], pgtype_to_datetime_sub(stmt, pgtype)); - set_nullfield_int4(&tuple[PROCOLS_CHAR_OCTET_LENGTH], pgtype_transfer_octet_length(stmt, pgtype, column_size)); + set_nullfield_int2(&tuple[PROCOLS_SQL_DATETIME_SUB], pgtype_to_datetime_sub(stmt, pgtype, PG_UNSPECIFIED)); + set_nullfield_int4(&tuple[PROCOLS_CHAR_OCTET_LENGTH], pgtype_attr_transfer_octet_length(conn, pgtype, PG_UNSPECIFIED, UNKNOWNS_AS_DEFAULT)); set_tuplefield_int4(&tuple[PROCOLS_ORDINAL_POSITION], j + 1); set_tuplefield_string(&tuple[PROCOLS_IS_NULLABLE], NULL_STRING); #endif /* ODBCVER >= 0x0300 */ @@ -5274,19 +5302,19 @@ mylog("atttypid=%s\n", atttypid ? atttypid : "(null)"); set_tuplefield_string(&tuple[PROCOLS_COLUMN_NAME], attname); set_tuplefield_int2(&tuple[PROCOLS_COLUMN_TYPE], SQL_RESULT_COL); set_tuplefield_int2(&tuple[PROCOLS_DATA_TYPE], pgtype_to_concise_type(stmt, typid, PG_STATIC)); - set_tuplefield_string(&tuple[PROCOLS_TYPE_NAME], pgtype_to_name(stmt, typid, FALSE)); + set_tuplefield_string(&tuple[PROCOLS_TYPE_NAME], pgtype_to_name(stmt, typid, PG_UNSPECIFIED, FALSE)); column_size = pgtype_column_size(stmt, typid, PG_STATIC, UNKNOWNS_AS_DEFAULT); set_nullfield_int4(&tuple[PROCOLS_COLUMN_SIZE], column_size); set_tuplefield_int4(&tuple[PROCOLS_BUFFER_LENGTH], pgtype_buffer_length(stmt, typid, PG_STATIC, UNKNOWNS_AS_DEFAULT)); set_nullfield_int2(&tuple[PROCOLS_DECIMAL_DIGITS], pgtype_decimal_digits(stmt, typid, PG_STATIC)); - set_nullfield_int2(&tuple[PROCOLS_NUM_PREC_RADIX], pgtype_radix(stmt, typid)); + set_nullfield_int2(&tuple[PROCOLS_NUM_PREC_RADIX], pgtype_radix(conn, typid)); set_tuplefield_int2(&tuple[PROCOLS_NULLABLE], SQL_NULLABLE_UNKNOWN); set_tuplefield_null(&tuple[PROCOLS_REMARKS]); #if (ODBCVER >= 0x0300) set_tuplefield_null(&tuple[PROCOLS_COLUMN_DEF]); set_nullfield_int2(&tuple[PROCOLS_SQL_DATA_TYPE], pgtype_to_sqldesctype(stmt, typid, PG_STATIC)); - set_nullfield_int2(&tuple[PROCOLS_SQL_DATETIME_SUB], pgtype_to_datetime_sub(stmt, typid)); - set_nullfield_int4(&tuple[PROCOLS_CHAR_OCTET_LENGTH], pgtype_transfer_octet_length(stmt, typid, column_size)); + set_nullfield_int2(&tuple[PROCOLS_SQL_DATETIME_SUB], pgtype_to_datetime_sub(stmt, typid, PG_UNSPECIFIED)); + set_nullfield_int4(&tuple[PROCOLS_CHAR_OCTET_LENGTH], pgtype_attr_transfer_octet_length(conn, typid, PG_UNSPECIFIED, UNKNOWNS_AS_DEFAULT)); set_tuplefield_int4(&tuple[PROCOLS_ORDINAL_POSITION], 0); set_tuplefield_string(&tuple[PROCOLS_IS_NULLABLE], NULL_STRING); #endif /* ODBCVER >= 0x0300 */ diff --git a/loadlib.c b/loadlib.c index 4d05d93..286bccc 100644 --- a/loadlib.c +++ b/loadlib.c @@ -88,7 +88,9 @@ CSTR gssapilib = "gssapi64"; CSTR gssapilib = "gssapi32"; #endif /* _WIN64 */ #ifndef NOT_USE_LIBPQ -CSTR checkproc = "PQconninfoParse"; +CSTR checkproc1 = "PQconnectdbParams"; +static int connect_withparam_available = -1; +CSTR checkproc2 = "PQconninfoParse"; static int sslverify_available = -1; #endif /* NOT_USE_LIBPQ */ @@ -156,11 +158,19 @@ DliErrorHook(unsigned dliNotify, if (hmodule = MODULE_load_from_psqlodbc_path(libpqlib), NULL == hmodule) hmodule = LoadLibrary(libpqlib); #ifndef NOT_USE_LIBPQ + if (NULL == hmodule) + connect_withparam_available = sslverify_available = FALSE; + if (connect_withparam_available < 0) + { + if (NULL == GetProcAddress(hmodule, checkproc1)) + connect_withparam_available = FALSE; + else + connect_withparam_available = TRUE; +inolog("connect_withparam_available=%d\n", connect_withparam_available); + } if (sslverify_available < 0) { - if (NULL == hmodule) - sslverify_available = FALSE; - else if (NULL == GetProcAddress(hmodule, checkproc)) + if (NULL == GetProcAddress(hmodule, checkproc2)) sslverify_available = FALSE; else sslverify_available = TRUE; @@ -304,7 +314,7 @@ BOOL ssl_verify_available(void) sslverify_available = 1; if (NULL != dlhandle) { - if (NULL == lt_dlsym(dlhandle, checkproc)) + if (NULL == lt_dlsym(dlhandle, checkproc2)) sslverify_available = 0; lt_dlclose(dlhandle); } @@ -315,6 +325,46 @@ BOOL ssl_verify_available(void) return (0 != sslverify_available); } +BOOL connect_with_param_available(void) +{ + if (connect_withparam_available < 0) + { +#if defined(_MSC_DELAY_LOAD_IMPORT) + __try { +#if (_MSC_VER < 1300) + __pfnDliFailureHook = DliErrorHook; + __pfnDliNotifyHook = DliErrorHook; +#else + __pfnDliFailureHook2 = DliErrorHook; + __pfnDliNotifyHook2 = DliErrorHook; +#endif /* _MSC_VER */ + PQescapeLiteral(NULL, NULL, 0); + } + __except (filter_env2encoding(GetExceptionCode())) { + } + if (connect_withparam_available < 0) +{ +inolog("connect_withparam_available is set to false\n"); + connect_withparam_available = 0; +} +#else +#ifdef HAVE_LIBLTDL + lt_dlhandle dlhandle = lt_dlopenext(libpqlib); + + connect_withparam_available = 1; + if (NULL != dlhandle) + { + if (NULL == lt_dlsym(dlhandle, checkproc1)) + connect_withparam_available = 0; + lt_dlclose(dlhandle); + } +#endif /* HAVE_LIBLTDL */ +#endif /* _MSC_DELAY_LOAD_IMPORT */ + } + + return (0 != connect_withparam_available); +} + void *CALL_PQconnectdb(const char *conninfo, BOOL *libpqLoaded) { void *pqconn = NULL; @@ -328,6 +378,7 @@ void *CALL_PQconnectdb(const char *conninfo, BOOL *libpqLoaded) __pfnDliFailureHook2 = DliErrorHook; __pfnDliNotifyHook2 = DliErrorHook; #endif /* _MSC_VER */ +inolog("calling PQconnectdb\n"); pqconn = PQconnectdb(conninfo); } __except ((GetExceptionCode() & 0xffff) == ERROR_MOD_NOT_FOUND ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { @@ -351,11 +402,53 @@ void *CALL_PQconnectdb(const char *conninfo, BOOL *libpqLoaded) #endif /* _MSC_DELAY_LOAD_IMPORT */ return pqconn; } + +void *CALL_PQconnectdbParams(const char *opts[], const char *vals[], BOOL *libpqLoaded) +{ + void *pqconn = NULL; + *libpqLoaded = TRUE; +#if defined(_MSC_DELAY_LOAD_IMPORT) + __try { +#if (_MSC_VER < 1300) + __pfnDliFailureHook = DliErrorHook; + __pfnDliNotifyHook = DliErrorHook; +#else + __pfnDliFailureHook2 = DliErrorHook; + __pfnDliNotifyHook2 = DliErrorHook; +#endif /* _MSC_VER */ +inolog("calling PQconnectdbParams\n"); + pqconn = PQconnectdbParams(opts, vals, 0); + } + __except ((GetExceptionCode() & 0xffff) == ERROR_MOD_NOT_FOUND ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH) { + *libpqLoaded = FALSE; + } +#if (_MSC_VER < 1300) + __pfnDliNotifyHook = NULL; +#else + __pfnDliNotifyHook2 = NULL; +#endif /* _MSC_VER */ + if (*libpqLoaded) + { + loaded_libpq = TRUE; + /* ssllibs are already loaded by libpq + if (PQgetssl(pqconn)) + loaded_ssllib = TRUE; + */ + } +#else + pqconn = PQconnectdbParams(opts, vals, 0); +#endif /* _MSC_DELAY_LOAD_IMPORT */ + return pqconn; +} #else BOOL ssl_verify_available(void) { return FALSE; } +BOOL connect_with_param_available(void) +{ + return FALSE; +} #endif /* NOT_USE_LIBPQ */ #ifdef _HANDLE_ENLIST_IN_DTC_ diff --git a/loadlib.h b/loadlib.h index 3cfd710..a1b5455 100644 --- a/loadlib.h +++ b/loadlib.h @@ -26,8 +26,10 @@ extern "C" { BOOL SSLLIB_check(void); #ifndef NOT_USE_LIBPQ void *CALL_PQconnectdb(const char *conninfo, BOOL *); +void *CALL_PQconnectdbParams(const char *opts[], const char *vals[], BOOL *); #endif /* NOT_USE_LIBPQ */ BOOL ssl_verify_available(void); +BOOL connect_with_param_available(void); #ifdef _HANDLE_ENLIST_IN_DTC_ RETCODE CALL_EnlistInDtc(ConnectionClass *conn, void * pTra, int method); RETCODE CALL_DtcOnDisconnect(ConnectionClass *); diff --git a/pgtypes.c b/pgtypes.c index db2bc59..9601a6c 100644 --- a/pgtypes.c +++ b/pgtypes.c @@ -27,7 +27,7 @@ #define EXPERIMENTAL_CURRENTLY -Int4 getCharColumnSize(StatementClass *stmt, OID type, int col, int handle_unknown_size_as); +Int4 getCharColumnSize(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as); /* * these are the types we support. all of the pgtype_ functions should @@ -110,6 +110,22 @@ SQLSMALLINT sqlTypes[] = { #if (ODBCVER >= 0x0350) SQL_GUID, #endif /* ODBCVER */ +/* AFAIK SQL_INTERVAL types cause troubles in some spplications */ +#ifdef PG_INTERVAL_AS_SQL_INTERVAL + SQL_INTERVAL_MONTH, + SQL_INTERVAL_YEAR, + SQL_INTERVAL_YEAR_TO_MONTH, + SQL_INTERVAL_DAY, + SQL_INTERVAL_HOUR, + SQL_INTERVAL_MINUTE, + SQL_INTERVAL_SECOND, + SQL_INTERVAL_DAY_TO_HOUR, + SQL_INTERVAL_DAY_TO_MINUTE, + SQL_INTERVAL_DAY_TO_SECOND, + SQL_INTERVAL_HOUR_TO_MINUTE, + SQL_INTERVAL_HOUR_TO_SECOND, + SQL_INTERVAL_MINUTE_TO_SECOND, +#endif /* PG_INTERVAL_AS_SQL_INTERVAL */ 0 }; @@ -132,189 +148,404 @@ pg_true_type(const ConnectionClass *conn, OID type, OID basetype) return basetype; } -OID -sqltype_to_pgtype(StatementClass *stmt, SQLSMALLINT fSqlType) -{ - OID pgType; - ConnectionClass *conn = SC_get_conn(stmt); - ConnInfo *ci = &(conn->connInfo); +#if (ODBCVER >= 0x0300) +#define MONTH_BIT (1 << 17) +#define YEAR_BIT (1 << 18) +#define DAY_BIT (1 << 19) +#define HOUR_BIT (1 << 26) +#define MINUTE_BIT (1 << 27) +#define SECOND_BIT (1 << 28) - pgType = 0; /* ??? */ - switch (fSqlType) +static SQLSMALLINT +get_interval_type(Int4 atttypmod, const char **name) +{ +mylog("!!! %s atttypmod=%x\n", __FUNCTION__, atttypmod); + if ((-1) == atttypmod) + return 0; + if (0 != (YEAR_BIT & atttypmod)) { - case SQL_BINARY: - pgType = PG_TYPE_BYTEA; - break; - - case SQL_CHAR: - pgType = PG_TYPE_BPCHAR; - break; - -#ifdef UNICODE_SUPPORT - case SQL_WCHAR: - pgType = PG_TYPE_BPCHAR; - break; -#endif /* UNICODE_SUPPORT */ - - case SQL_BIT: - pgType = ci->drivers.bools_as_char ? PG_TYPE_CHAR : PG_TYPE_BOOL; - break; + if (0 != (MONTH_BIT & atttypmod)) + { + if (name) + *name = "interval year to month"; + return SQL_INTERVAL_YEAR_TO_MONTH; + } + if (name) + *name = "interval year"; + return SQL_INTERVAL_YEAR; + } + else if (0 != (MONTH_BIT & atttypmod)) + { + if (name) + *name = "interval month"; + return SQL_INTERVAL_MONTH; + } + else if (0 != (DAY_BIT & atttypmod)) + { + if (0 != (SECOND_BIT & atttypmod)) + { + if (name) + *name = "interval day to second"; + return SQL_INTERVAL_DAY_TO_SECOND; + } + else if (0 != (MINUTE_BIT & atttypmod)) + { + if (name) + *name = "interval day to minute"; + return SQL_INTERVAL_DAY_TO_MINUTE; + } + else if (0 != (HOUR_BIT & atttypmod)) + { + if (name) + *name = "interval day to hour"; + return SQL_INTERVAL_DAY_TO_HOUR; + } + if (name) + *name = "interval day"; + return SQL_INTERVAL_DAY; + } + else if (0 != (HOUR_BIT & atttypmod)) + { + if (0 != (SECOND_BIT & atttypmod)) + { + if (name) + *name = "interval hour to second"; + return SQL_INTERVAL_HOUR_TO_SECOND; + } + else if (0 != (MINUTE_BIT & atttypmod)) + { + if (name) + *name = "interval hour to minute"; + return SQL_INTERVAL_HOUR_TO_MINUTE; + } + if (name) + *name = "interval hour"; + return SQL_INTERVAL_HOUR; + } + else if (0 != (MINUTE_BIT & atttypmod)) + { + if (0 != (SECOND_BIT & atttypmod)) + { + if (name) + *name = "interval minute to second"; + return SQL_INTERVAL_MINUTE_TO_SECOND; + } + if (name) + *name = "interval minute"; + return SQL_INTERVAL_MINUTE; + } + else if (0 != (SECOND_BIT & atttypmod)) + { + if (name) + *name = "interval second"; + return SQL_INTERVAL_SECOND; + } -#if (ODBCVER >= 0x0300) - case SQL_TYPE_DATE: + if (name) + *name = "interval"; + return 0; +} #endif /* ODBCVER */ - case SQL_DATE: - pgType = PG_TYPE_DATE; - break; - case SQL_DOUBLE: - case SQL_FLOAT: - pgType = PG_TYPE_FLOAT8; - break; +static Int4 +getCharColumnSizeX(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as) +{ + int p = -1, maxsize; + QResultClass *result; + const ConnInfo *ci = &(conn->connInfo); - case SQL_DECIMAL: - case SQL_NUMERIC: - pgType = PG_TYPE_NUMERIC; - break; + mylog("%s: type=%d, atttypmod=%d, adtsize_or=%d, unknown = %d\n", __FUNCTION__, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); - case SQL_BIGINT: - pgType = PG_TYPE_INT8; + /* Assign Maximum size based on parameters */ + switch (type) + { + case PG_TYPE_TEXT: + if (ci->drivers.text_as_longvarchar) + maxsize = ci->drivers.max_longvarchar_size; + else + maxsize = ci->drivers.max_varchar_size; break; - case SQL_INTEGER: - pgType = PG_TYPE_INT4; + case PG_TYPE_VARCHAR: + case PG_TYPE_BPCHAR: + maxsize = ci->drivers.max_varchar_size; break; - case SQL_LONGVARBINARY: - if (ci->bytea_as_longvarbinary) - pgType = PG_TYPE_BYTEA; + default: + if (ci->drivers.unknowns_as_longvarchar) + maxsize = ci->drivers.max_longvarchar_size; else - pgType = conn->lobj_type; - break; - - case SQL_LONGVARCHAR: - pgType = ci->drivers.text_as_longvarchar ? PG_TYPE_TEXT : PG_TYPE_VARCHAR; + maxsize = ci->drivers.max_varchar_size; break; - + } #ifdef UNICODE_SUPPORT - case SQL_WLONGVARCHAR: - pgType = ci->drivers.text_as_longvarchar ? PG_TYPE_TEXT : PG_TYPE_VARCHAR; - break; + if (CC_is_in_unicode_driver(conn) && + isSqlServr() && + maxsize > 4000) + maxsize = 4000; #endif /* UNICODE_SUPPORT */ - case SQL_REAL: - pgType = PG_TYPE_FLOAT4; - break; + if (maxsize == TEXT_FIELD_SIZE + 1) /* magic length for testing */ + { + if (PG_VERSION_GE(conn, 7.1)) + maxsize = 0; + else + maxsize = TEXT_FIELD_SIZE; + } + /* + * Static ColumnSize (i.e., the Maximum ColumnSize of the datatype) This + * has nothing to do with a result set. + */ +inolog("!!! atttypmod < 0 ?\n"); + if (atttypmod < 0 && adtsize_or_longestlen < 0) + return maxsize; - case SQL_SMALLINT: - case SQL_TINYINT: - pgType = PG_TYPE_INT2; - break; +/* +inolog("!!! Curres == NULL?\n"); + if (result = SC_get_Curres(stmt), NULL == result) + return maxsize; +*/ - case SQL_TIME: -#if (ODBCVER >= 0x0300) - case SQL_TYPE_TIME: -#endif /* ODBCVER */ - pgType = PG_TYPE_TIME; - break; + /* + * Catalog Result Sets -- use assigned column width (i.e., from + * set_tuplefield_string) + */ +inolog("!!! catalog_result=%d\n", handle_unknown_size_as); + if (UNKNOWNS_AS_CATALOG == handle_unknown_size_as) + { + if (adtsize_or_longestlen > 0) + return adtsize_or_longestlen; + return maxsize; + } - case SQL_TIMESTAMP: +inolog("!!! adtsize_or_logngest=%d\n", adtsize_or_longestlen); + p = adtsize_or_longestlen; /* longest */ + /* Size is unknown -- handle according to parameter */ + if (atttypmod > 0) /* maybe the length is known */ + { + if (atttypmod >= p) + return atttypmod; + switch (type) + { + case PG_TYPE_VARCHAR: + case PG_TYPE_BPCHAR: #if (ODBCVER >= 0x0300) - case SQL_TYPE_TIMESTAMP: + return atttypmod; +#else + if (CC_is_in_unicode_driver(conn) || conn->ms_jet) + return atttypmod; + return p; #endif /* ODBCVER */ - pgType = PG_TYPE_DATETIME; - break; - - case SQL_VARBINARY: - pgType = PG_TYPE_BYTEA; - break; - - case SQL_VARCHAR: - pgType = PG_TYPE_VARCHAR; - break; + } + } -#if UNICODE_SUPPORT - case SQL_WVARCHAR: - pgType = PG_TYPE_VARCHAR; + /* The type is really unknown */ + switch (handle_unknown_size_as) + { + case UNKNOWNS_AS_DONTKNOW: + return -1; + case UNKNOWNS_AS_LONGEST: + mylog("%s: LONGEST: p = %d\n", __FUNCTION__, p); + if (p > 0) + return p; break; -#endif /* UNICODE_SUPPORT */ - -#if (ODBCVER >= 0x0350) - case SQL_GUID: - if (PG_VERSION_GE(conn, 8.3)) - pgType = PG_TYPE_UUID; + case UNKNOWNS_AS_MAX: break; -#endif /* ODBCVER */ - + default: + return -1; + } + if (maxsize <= 0) + return maxsize; + switch (type) + { + case PG_TYPE_BPCHAR: + case PG_TYPE_VARCHAR: + case PG_TYPE_TEXT: + return maxsize; } - return pgType; + if (p > maxsize) + maxsize = p; + return maxsize; } - -/* - * There are two ways of calling this function: - * - * 1. When going through the supported PG types (SQLGetTypeInfo) - * - * 2. When taking any type id (SQLColumns, SQLGetData) - * - * The first type will always work because all the types defined are returned here. - * The second type will return a default based on global parameter when it does not - * know. This allows for supporting - * types that are unknown. All other pg routines in here return a suitable default. - */ -SQLSMALLINT -pgtype_to_concise_type(StatementClass *stmt, OID type, int col) +static SQLSMALLINT +getNumericDecimalDigitsX(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longest, int handle_unknown_size_as) { - ConnectionClass *conn = SC_get_conn(stmt); - ConnInfo *ci = &(conn->connInfo); -#if (ODBCVER >= 0x0300) - EnvironmentClass *env = (EnvironmentClass *) CC_get_env(conn); -#endif /* ODBCVER */ + Int4 default_decimal_digits = 6; - switch (type) - { - case PG_TYPE_CHAR: - case PG_TYPE_CHAR2: - case PG_TYPE_CHAR4: - case PG_TYPE_CHAR8: - return ALLOW_WCHAR(conn) ? SQL_WCHAR : SQL_CHAR; - case PG_TYPE_NAME: - return ALLOW_WCHAR(conn) ? SQL_WVARCHAR : SQL_VARCHAR; + mylog("%s: type=%d, atttypmod=%d\n", __FUNCTION__, type, atttypmod); -#ifdef UNICODE_SUPPORT - case PG_TYPE_BPCHAR: - if (col >= 0 && - getCharColumnSize(stmt, type, col, UNKNOWNS_AS_MAX) > ci->drivers.max_varchar_size) - return ALLOW_WCHAR(conn) ? SQL_WLONGVARCHAR : SQL_LONGVARCHAR; - return ALLOW_WCHAR(conn) ? SQL_WCHAR : SQL_CHAR; + if (atttypmod < 0 && adtsize_or_longest < 0) + return default_decimal_digits; - case PG_TYPE_VARCHAR: - if (col >= 0 && - getCharColumnSize(stmt, type, col, UNKNOWNS_AS_MAX) > ci->drivers.max_varchar_size) - return ALLOW_WCHAR(conn) ? SQL_WLONGVARCHAR : SQL_LONGVARCHAR; - return ALLOW_WCHAR(conn) ? SQL_WVARCHAR : SQL_VARCHAR; + if (atttypmod > -1) + return (atttypmod & 0xffff); + if (adtsize_or_longest <= 0) + return default_decimal_digits; + if (UNKNOWNS_AS_CATALOG != handle_unknown_size_as) + { + if (adtsize_or_longest < 5) + adtsize_or_longest = 5; + } + return adtsize_or_longest; +} - case PG_TYPE_TEXT: - return ci->drivers.text_as_longvarchar ? - (ALLOW_WCHAR(conn) ? SQL_WLONGVARCHAR : SQL_LONGVARCHAR) : - (ALLOW_WCHAR(conn) ? SQL_WVARCHAR : SQL_VARCHAR); +static Int4 /* PostgreSQL restritiction */ +getNumericColumnSizeX(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longest, int handle_unknown_size_as) +{ + Int4 default_column_size = 28; -#else - case PG_TYPE_BPCHAR: - if (col >= 0 && - getCharColumnSize(stmt, type, col, UNKNOWNS_AS_MAX) > ci->drivers.max_varchar_size) - return SQL_LONGVARCHAR; - return SQL_CHAR; + mylog("%s: type=%d, typmod=%d\n", __FUNCTION__, type, atttypmod); - case PG_TYPE_VARCHAR: - if (col >= 0 && - getCharColumnSize(stmt, type, col, UNKNOWNS_AS_MAX) > ci->drivers.max_varchar_size) - return SQL_LONGVARCHAR; - return SQL_VARCHAR; + if (atttypmod < 0 && adtsize_or_longest < 0) + return default_column_size; - case PG_TYPE_TEXT: + if (atttypmod > -1) + return (atttypmod >> 16) & 0xffff; + if (adtsize_or_longest <= 0) + return default_column_size; + adtsize_or_longest *= 2; + if (UNKNOWNS_AS_CATALOG != handle_unknown_size_as) + { + if (adtsize_or_longest < 10) + adtsize_or_longest = 10; + } + return adtsize_or_longest; +} + +static SQLSMALLINT +getTimestampDecimalDigitsX(const ConnectionClass *conn, OID type, int atttypmod) +{ + mylog("%s: type=%d, atttypmod=%d\n", __FUNCTION__, type, atttypmod); + + if (PG_VERSION_LT(conn, 7.2)) + return 0; + + return (atttypmod > -1 ? atttypmod : 6); +} + + +static SQLSMALLINT +getTimestampColumnSizeX(const ConnectionClass *conn, OID type, int atttypmod) +{ + Int4 fixed, scale; + + mylog("%s: type=%d, atttypmod=%d\n", __FUNCTION__, type, atttypmod); + + 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: + if (USE_ZONE) + fixed = 22; + else + fixed = 19; + break; + } + scale = getTimestampDecimalDigitsX(conn, type, atttypmod); + return (scale > 0) ? fixed + 1 + scale : fixed; +} + +static SQLSMALLINT +getIntervalDecimalDigits(OID type, int atttypmod) +{ + Int4 prec; + + mylog("%s: type=%d, atttypmod=%d\n", __FUNCTION__, type, atttypmod); + + if ((atttypmod & SECOND_BIT) == 0) + return 0; + return (prec = atttypmod & 0xffff) == 0xffff ? 6 : prec ; +} + +static SQLSMALLINT +getIntervalColumnSize(OID type, int atttypmod) +{ + Int4 ttl, leading_precision = 9, scale; + + mylog("%s: type=%d, atttypmod=%d\n", __FUNCTION__, type, atttypmod); + + ttl = leading_precision; + switch (get_interval_type(atttypmod, NULL)) + { + case SQL_INTERVAL_DAY_TO_SECOND: + ttl += 9; + break; + case SQL_INTERVAL_HOUR_TO_SECOND: + case SQL_INTERVAL_DAY_TO_MINUTE: + ttl += 6; + break; + case SQL_INTERVAL_MINUTE_TO_SECOND: + case SQL_INTERVAL_HOUR_TO_MINUTE: + case SQL_INTERVAL_DAY_TO_HOUR: + case SQL_INTERVAL_YEAR_TO_MONTH: + ttl += 3; + break; + } + scale = getIntervalDecimalDigits(type, atttypmod); + return (scale > 0) ? ttl + 1 + scale : ttl; +} + + +SQLSMALLINT +pgtype_attr_to_concise_type(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen) +{ + const ConnInfo *ci = &(conn->connInfo); +#if (ODBCVER >= 0x0300) + EnvironmentClass *env = (EnvironmentClass *) CC_get_env(conn); +#ifdef PG_INTERVAL_AS_SQL_INTERVAL + SQLSMALLINT sqltype; +#endif /* PG_INTERVAL_AS_SQL_INTERVAL */ +#endif /* ODBCVER */ + + switch (type) + { + case PG_TYPE_CHAR: + case PG_TYPE_CHAR2: + case PG_TYPE_CHAR4: + case PG_TYPE_CHAR8: + return ALLOW_WCHAR(conn) ? SQL_WCHAR : SQL_CHAR; + case PG_TYPE_NAME: + case PG_TYPE_REFCURSOR: + return ALLOW_WCHAR(conn) ? SQL_WVARCHAR : SQL_VARCHAR; + +#ifdef UNICODE_SUPPORT + case PG_TYPE_BPCHAR: + if (getCharColumnSizeX(conn, type, atttypmod, adtsize_or_longestlen, UNKNOWNS_AS_DEFAULT) > ci->drivers.max_varchar_size) + return ALLOW_WCHAR(conn) ? SQL_WLONGVARCHAR : SQL_LONGVARCHAR; + return ALLOW_WCHAR(conn) ? SQL_WCHAR : SQL_CHAR; + + case PG_TYPE_VARCHAR: + if (getCharColumnSizeX(conn, type, atttypmod, adtsize_or_longestlen, UNKNOWNS_AS_DEFAULT) > ci->drivers.max_varchar_size) + return ALLOW_WCHAR(conn) ? SQL_WLONGVARCHAR : SQL_LONGVARCHAR; + return ALLOW_WCHAR(conn) ? SQL_WVARCHAR : SQL_VARCHAR; + + case PG_TYPE_TEXT: + return ci->drivers.text_as_longvarchar ? + (ALLOW_WCHAR(conn) ? SQL_WLONGVARCHAR : SQL_LONGVARCHAR) : + (ALLOW_WCHAR(conn) ? SQL_WVARCHAR : SQL_VARCHAR); + +#else + case PG_TYPE_BPCHAR: + if (getCharColumnSizeX(conn, type, atttypmod, adtsize_or_longestlen, UNKNOWNS_AS_DEFAULT) > ci->drivers.max_varchar_size) + return SQL_LONGVARCHAR; + return SQL_CHAR; + + case PG_TYPE_VARCHAR: + if (getCharColumnSizeX(conn, type, atttypmod, adtsize_or_longestlen, UNKNOWNS_AS_DEFAULT) > ci->drivers.max_varchar_size) + return SQL_LONGVARCHAR; + return SQL_VARCHAR; + + case PG_TYPE_TEXT: return ci->drivers.text_as_longvarchar ? SQL_LONGVARCHAR : SQL_VARCHAR; #endif /* UNICODE_SUPPORT */ @@ -389,6 +620,14 @@ pgtype_to_concise_type(StatementClass *stmt, OID type, int col) return SQL_GUID; #endif /* ODBCVER */ return CC_is_in_unicode_driver(conn) ? SQL_WVARCHAR : SQL_VARCHAR; + + case PG_TYPE_INTERVAL: +#ifdef PG_INTERVAL_AS_SQL_INTERVAL + if (sqltype = get_interval_type(atttypmod, NULL), 0 != sqltype) + return sqltype; +#endif /* PG_INTERVAL_AS_SQL_INTERVAL */ + return CC_is_in_unicode_driver(conn) ? SQL_WVARCHAR : SQL_VARCHAR; + default: /* @@ -409,11 +648,15 @@ pgtype_to_concise_type(StatementClass *stmt, OID type, int col) } SQLSMALLINT -pgtype_to_sqldesctype(StatementClass *stmt, OID type, int col) +pgtype_attr_to_sqldesctype(const ConnectionClass *conn, OID type, int atttypmod) { SQLSMALLINT rettype; - switch (rettype = pgtype_to_concise_type(stmt, type, col)) +#ifdef PG_INTERVAL_AS_SQL_INTERVAL + if (PG_TYPE_INTERVAL == type) + return SQL_INTERVAL; +#endif /* PG_INTERVAL_AS_SQL_INTERVAL */ + switch (rettype = pgtype_attr_to_concise_type(conn, type, atttypmod, PG_UNSPECIFIED)) { #if (ODBCVER >= 0x0300) case SQL_TYPE_DATE: @@ -426,9 +669,11 @@ pgtype_to_sqldesctype(StatementClass *stmt, OID type, int col) } SQLSMALLINT -pgtype_to_datetime_sub(StatementClass *stmt, OID type) +pgtype_attr_to_datetime_sub(const ConnectionClass *conn, OID type, int atttypmod) { - switch (pgtype_to_concise_type(stmt, type, PG_STATIC)) + SQLSMALLINT rettype; + + switch (rettype = pgtype_attr_to_concise_type(conn, type, atttypmod, PG_UNSPECIFIED)) { #if (ODBCVER >= 0x0300) case SQL_TYPE_DATE: @@ -437,19 +682,34 @@ pgtype_to_datetime_sub(StatementClass *stmt, OID type) return SQL_CODE_TIME; case SQL_TYPE_TIMESTAMP: return SQL_CODE_TIMESTAMP; + case SQL_INTERVAL_MONTH: + case SQL_INTERVAL_YEAR: + case SQL_INTERVAL_YEAR_TO_MONTH: + case SQL_INTERVAL_DAY: + case SQL_INTERVAL_HOUR: + case SQL_INTERVAL_MINUTE: + case SQL_INTERVAL_SECOND: + case SQL_INTERVAL_DAY_TO_HOUR: + case SQL_INTERVAL_DAY_TO_MINUTE: + case SQL_INTERVAL_DAY_TO_SECOND: + case SQL_INTERVAL_HOUR_TO_MINUTE: + case SQL_INTERVAL_HOUR_TO_SECOND: + case SQL_INTERVAL_MINUTE_TO_SECOND: + return rettype - 100; #endif /* ODBCVER */ } - return -1; + return rettype; } - SQLSMALLINT -pgtype_to_ctype(StatementClass *stmt, OID type) +pgtype_attr_to_ctype(const ConnectionClass *conn, OID type, int atttypmod) { - ConnectionClass *conn = SC_get_conn(stmt); - ConnInfo *ci = &(conn->connInfo); + const ConnInfo *ci = &(conn->connInfo); #if (ODBCVER >= 0x0300) EnvironmentClass *env = (EnvironmentClass *) CC_get_env(conn); +#ifdef PG_INTERVAL_AS_SQL_INTERVAL + SQLSMALLINT ctype; +#endif /* PG_INTERVAL_A_SQL_INTERVAL */ #endif /* ODBCVER */ switch (type) @@ -507,11 +767,7 @@ pgtype_to_ctype(StatementClass *stmt, OID type) case PG_TYPE_BPCHAR: case PG_TYPE_VARCHAR: case PG_TYPE_TEXT: - if (CC_is_in_unicode_driver(conn) -#ifdef NOT_USED - && ! stmt->catalog_result -#endif /* NOT USED */ - ) + if (CC_is_in_unicode_driver(conn)) return SQL_C_WCHAR; return SQL_C_CHAR; #endif /* UNICODE_SUPPORT */ @@ -520,115 +776,720 @@ pgtype_to_ctype(StatementClass *stmt, OID type) if (!conn->ms_jet) return SQL_C_GUID; #endif /* ODBCVER */ - return SQL_C_CHAR; + return SQL_C_CHAR; + + case PG_TYPE_INTERVAL: +#ifdef PG_INTERVAL_AS_SQL_INTERVAL + if (ctype = get_interval_type(atttypmod, NULL), 0 != ctype) + return ctype; +#endif /* PG_INTERVAL_AS_SQL_INTERVAL */ + return CC_is_in_unicode_driver(conn) ? SQL_C_WCHAR : SQL_CHAR; + + default: + /* hack until permanent type is available */ + if (type == conn->lobj_type) + return SQL_C_BINARY; + + /* Experimental, Does this work ? */ +#ifdef EXPERIMENTAL_CURRENTLY + if (ALLOW_WCHAR(conn)) + return SQL_C_WCHAR; +#endif /* EXPERIMENTAL_CURRENTLY */ + return SQL_C_CHAR; + } +} + +const char * +pgtype_attr_to_name(const ConnectionClass *conn, OID type, int atttypmod, BOOL auto_increment) +{ + const char *tname = NULL; + + switch (type) + { + case PG_TYPE_CHAR: + return "char"; + case PG_TYPE_CHAR2: + return "char2"; + case PG_TYPE_CHAR4: + return "char4"; + case PG_TYPE_CHAR8: + return "char8"; + case PG_TYPE_INT8: + return auto_increment ? "bigserial" : "int8"; + case PG_TYPE_NUMERIC: + return "numeric"; + case PG_TYPE_VARCHAR: + return "varchar"; + case PG_TYPE_BPCHAR: + return "char"; + case PG_TYPE_TEXT: + return "text"; + case PG_TYPE_NAME: + return "name"; + case PG_TYPE_REFCURSOR: + return "refcursor"; + case PG_TYPE_INT2: + return "int2"; + case PG_TYPE_OID: + return OID_NAME; + case PG_TYPE_XID: + return "xid"; + case PG_TYPE_INT4: +inolog("pgtype_to_name int4\n"); + return auto_increment ? "serial" : "int4"; + case PG_TYPE_FLOAT4: + return "float4"; + case PG_TYPE_FLOAT8: + return "float8"; + case PG_TYPE_DATE: + return "date"; + case PG_TYPE_TIME: + return "time"; + case PG_TYPE_ABSTIME: + return "abstime"; + case PG_TYPE_DATETIME: + if (PG_VERSION_GT(conn, 7.1)) + return "timestamptz"; + else if (PG_VERSION_LT(conn, 7.0)) + return "datetime"; + else + return "timestamp"; + case PG_TYPE_TIMESTAMP_NO_TMZONE: + return "timestamp without time zone"; + case PG_TYPE_TIMESTAMP: + return "timestamp"; + case PG_TYPE_MONEY: + return "money"; + case PG_TYPE_BOOL: + return "bool"; + case PG_TYPE_BYTEA: + return "bytea"; + case PG_TYPE_XML: + return "xml"; + case PG_TYPE_MACADDR: + return "macaddr"; + case PG_TYPE_INET: + return "inet"; + case PG_TYPE_CIDR: + return "cidr"; + case PG_TYPE_UUID: + return "uuid"; +#if (ODBCVER >= 0x0300) + case PG_TYPE_INTERVAL: + get_interval_type(atttypmod, &tname); + return tname; +#endif /* ODBCVER */ + + case PG_TYPE_LO_UNDEFINED: + return PG_TYPE_LO_NAME; + + default: + /* hack until permanent type is available */ + if (type == conn->lobj_type) + return PG_TYPE_LO_NAME; + + /* + * "unknown" can actually be used in alter table because it is + * a real PG type! + */ + return "unknown"; + } +} + + +Int4 /* PostgreSQL restriction */ +pgtype_attr_column_size(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longest, int handle_unknown_size_as) +{ + const ConnInfo *ci = &(conn->connInfo); + + if (handle_unknown_size_as == UNKNOWNS_AS_DEFAULT) + handle_unknown_size_as = ci->drivers.unknown_sizes; + switch (type) + { + case PG_TYPE_CHAR: + return 1; + case PG_TYPE_CHAR2: + return 2; + case PG_TYPE_CHAR4: + return 4; + case PG_TYPE_CHAR8: + return 8; + + case PG_TYPE_NAME: + case PG_TYPE_REFCURSOR: + { + int value = 0; + if (PG_VERSION_GT(conn, 7.4)) + value = CC_get_max_idlen(conn); +#ifdef NAME_FIELD_SIZE + else + value = NAME_FIELD_SIZE; +#endif /* NAME_FIELD_SIZE */ + if (0 == value) + { + if (PG_VERSION_GE(conn, 7.3)) + value = NAMEDATALEN_V73; + else + value = NAMEDATALEN_V72; + } + return value; + } + + case PG_TYPE_INT2: + return 5; + + case PG_TYPE_OID: + case PG_TYPE_XID: + case PG_TYPE_INT4: + return 10; + + case PG_TYPE_INT8: + return 19; /* signed */ + + case PG_TYPE_NUMERIC: + return getNumericColumnSizeX(conn, type, atttypmod, adtsize_or_longest, handle_unknown_size_as); + + case PG_TYPE_FLOAT4: + case PG_TYPE_MONEY: + return 7; + + case PG_TYPE_FLOAT8: + return 15; + + case PG_TYPE_DATE: + return 10; + case PG_TYPE_TIME: + return 8; + + case PG_TYPE_ABSTIME: + case PG_TYPE_TIMESTAMP: + return 22; + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP_NO_TMZONE: + /* return 22; */ + return getTimestampColumnSizeX(conn, type, atttypmod); + + case PG_TYPE_BOOL: + return ci->drivers.bools_as_char ? PG_WIDTH_OF_BOOLS_AS_CHAR : 1; + + case PG_TYPE_MACADDR: + return 17; + + case PG_TYPE_INET: + case PG_TYPE_CIDR: + return sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128"); + case PG_TYPE_UUID: + return sizeof("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); + + case PG_TYPE_LO_UNDEFINED: + return SQL_NO_TOTAL; + + case PG_TYPE_INTERVAL: + return getIntervalColumnSize(type, atttypmod); + + default: + + if (type == conn->lobj_type) /* hack until permanent + * type is available */ + return SQL_NO_TOTAL; + if (PG_TYPE_BYTEA == type && ci->bytea_as_longvarbinary) + return SQL_NO_TOTAL; + + /* Handle Character types and unknown types */ + return getCharColumnSizeX(conn, type, atttypmod, adtsize_or_longest, handle_unknown_size_as); + } +} + +SQLSMALLINT +pgtype_attr_precision(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longest, int handle_unknown_size_as) +{ + switch (type) + { + case PG_TYPE_NUMERIC: + return getNumericColumnSizeX(conn, type, atttypmod, adtsize_or_longest, handle_unknown_size_as); + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP_NO_TMZONE: + return getTimestampDecimalDigitsX(conn, type, atttypmod); +#ifdef PG_INTERVAL_AS_SQL_INTERVAL + case PG_TYPE_INTERVAL: + return getIntervalDecimalDigits(type, atttypmod); +#endif /* PG_INTERVAL_AS_SQL_INTERVAL */ + } + return -1; +} + +Int4 +pgtype_attr_display_size(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as) +{ + int dsize; + + switch (type) + { + case PG_TYPE_INT2: + return 6; + + case PG_TYPE_OID: + case PG_TYPE_XID: + return 10; + + case PG_TYPE_INT4: + return 11; + + case PG_TYPE_INT8: + return 20; /* signed: 19 digits + sign */ + + case PG_TYPE_NUMERIC: + dsize = getNumericColumnSizeX(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); + return dsize < 0 ? dsize : dsize + 2; + + case PG_TYPE_MONEY: + return 15; /* ($9,999,999.99) */ + + case PG_TYPE_FLOAT4: + return 13; + + case PG_TYPE_FLOAT8: + return 22; + +#if (ODBCVER >= 0x0350) + case PG_TYPE_UUID: + return 36; +#endif /* ODBCVER */ + + case PG_TYPE_INTERVAL: + return 30; + + /* Character types use regular precision */ + default: + return pgtype_attr_column_size(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); + } +} + +Int4 +pgtype_attr_buffer_length(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as) +{ + switch (type) + { + case PG_TYPE_INT2: + return 2; /* sizeof(SQLSMALLINT) */ + + case PG_TYPE_OID: + case PG_TYPE_XID: + case PG_TYPE_INT4: + return 4; /* sizeof(SQLINTEGER) */ + + case PG_TYPE_INT8: + if (SQL_C_CHAR == pgtype_attr_to_ctype(conn, type, atttypmod)) + return 20; /* signed: 19 digits + sign */ + return 8; /* sizeof(SQLSBININT) */ + + case PG_TYPE_NUMERIC: + return getNumericColumnSizeX(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as) + 2; + + case PG_TYPE_FLOAT4: + case PG_TYPE_MONEY: + return 4; /* sizeof(SQLREAL) */ + + case PG_TYPE_FLOAT8: + return 8; /* sizeof(SQLFLOAT) */ + + case PG_TYPE_DATE: + case PG_TYPE_TIME: + return 6; /* sizeof(DATE(TIME)_STRUCT) */ + + case PG_TYPE_ABSTIME: + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP: + case PG_TYPE_TIMESTAMP_NO_TMZONE: + return 16; /* sizeof(TIMESTAMP_STRUCT) */ + + case PG_TYPE_UUID: + return 16; /* sizeof(SQLGUID) */ + + /* Character types use the default precision */ + case PG_TYPE_VARCHAR: + case PG_TYPE_BPCHAR: + { + int coef = 1; + Int4 prec = pgtype_attr_column_size(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as), maxvarc; + if (SQL_NO_TOTAL == prec) + return prec; +#ifdef UNICODE_SUPPORT + if (CC_is_in_unicode_driver(conn)) + return prec * WCLEN; +#endif /* UNICODE_SUPPORT */ + /* after 7.2 */ + if (PG_VERSION_GE(conn, 7.2)) + coef = conn->mb_maxbyte_per_char; + if (coef < 2 && (conn->connInfo).lf_conversion) + /* CR -> CR/LF */ + coef = 2; + if (coef == 1) + return prec; + maxvarc = conn->connInfo.drivers.max_varchar_size; + if (prec <= maxvarc && prec * coef > maxvarc) + return maxvarc; + return coef * prec; + } +#ifdef PG_INTERVAL_AS_SQL_INTERVAL + case PG_TYPE_INTERVAL: + return sizeof(SQL_INTERVAL_STRUCT); +#endif /* PG_INTERVAL_AS_SQL_INTERVAL */ + + default: + return pgtype_attr_column_size(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); + } +} + +/* + */ +Int4 +pgtype_attr_desclength(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as) +{ + switch (type) + { + case PG_TYPE_INT2: + return 2; + + case PG_TYPE_OID: + case PG_TYPE_XID: + case PG_TYPE_INT4: + return 4; + + case PG_TYPE_INT8: + return 20; /* signed: 19 digits + sign */ + + case PG_TYPE_NUMERIC: + return getNumericColumnSizeX(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as) + 2; + + case PG_TYPE_FLOAT4: + case PG_TYPE_MONEY: + return 4; + + case PG_TYPE_FLOAT8: + return 8; + + case PG_TYPE_DATE: + case PG_TYPE_TIME: + case PG_TYPE_ABSTIME: + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP_NO_TMZONE: + case PG_TYPE_TIMESTAMP: + case PG_TYPE_VARCHAR: + case PG_TYPE_BPCHAR: + return pgtype_attr_column_size(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); + default: + return pgtype_attr_column_size(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); + } +} + +Int2 +pgtype_attr_decimal_digits(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as) +{ + switch (type) + { + case PG_TYPE_INT2: + case PG_TYPE_OID: + case PG_TYPE_XID: + case PG_TYPE_INT4: + case PG_TYPE_INT8: + case PG_TYPE_FLOAT4: + case PG_TYPE_FLOAT8: + case PG_TYPE_MONEY: + case PG_TYPE_BOOL: + + /* + * Number of digits to the right of the decimal point in + * "yyyy-mm=dd hh:mm:ss[.f...]" + */ + case PG_TYPE_ABSTIME: + case PG_TYPE_TIMESTAMP: + return 0; + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP_NO_TMZONE: + /* return 0; */ + return getTimestampDecimalDigitsX(conn, type, atttypmod); + + case PG_TYPE_NUMERIC: + return getNumericDecimalDigitsX(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); + +#ifdef PG_INTERVAL_AS_SQL_INTERVAL + case PG_TYPE_INTERVAL: + return getIntervalDecimalDigits(type, atttypmod); +#endif /* PG_INTERVAL_AS_SQL_INTERVAL */ + + default: + return -1; + } +} + +Int2 +pgtype_attr_scale(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as) +{ + switch (type) + { + case PG_TYPE_NUMERIC: + return getNumericDecimalDigitsX(conn, type, atttypmod, adtsize_or_longestlen, handle_unknown_size_as); + } + return -1; +} + +Int4 +pgtype_attr_transfer_octet_length(const ConnectionClass *conn, OID type, int atttypmod, int handle_unknown_size_as) +{ + int coef = 1; + Int4 maxvarc, column_size; + switch (type) + { + case PG_TYPE_VARCHAR: + case PG_TYPE_BPCHAR: + case PG_TYPE_TEXT: + column_size = pgtype_attr_column_size(conn, type, atttypmod, PG_UNSPECIFIED, handle_unknown_size_as); + if (SQL_NO_TOTAL == column_size) + return column_size; +#ifdef UNICODE_SUPPORT + if (CC_is_in_unicode_driver(conn)) + return column_size * WCLEN; +#endif /* UNICODE_SUPPORT */ + /* after 7.2 */ + if (PG_VERSION_GE(conn, 7.2)) + coef = conn->mb_maxbyte_per_char; + if (coef < 2 && (conn->connInfo).lf_conversion) + /* CR -> CR/LF */ + coef = 2; + if (coef == 1) + return column_size; + maxvarc = conn->connInfo.drivers.max_varchar_size; + if (column_size <= maxvarc && column_size * coef > maxvarc) + return maxvarc; + return coef * column_size; + case PG_TYPE_BYTEA: + return pgtype_attr_column_size(conn, type, atttypmod, PG_UNSPECIFIED, handle_unknown_size_as); + default: + if (type == conn->lobj_type) + return pgtype_attr_column_size(conn, type, atttypmod, PG_UNSPECIFIED, handle_unknown_size_as); + } + return -1; +} + + +OID +sqltype_to_pgtype(const ConnectionClass *conn, SQLSMALLINT fSqlType) +{ + OID pgType; + const ConnInfo *ci = &(conn->connInfo); + + pgType = 0; /* ??? */ + switch (fSqlType) + { + case SQL_BINARY: + pgType = PG_TYPE_BYTEA; + break; + + case SQL_CHAR: + pgType = PG_TYPE_BPCHAR; + break; + +#ifdef UNICODE_SUPPORT + case SQL_WCHAR: + pgType = PG_TYPE_BPCHAR; + break; +#endif /* UNICODE_SUPPORT */ + + case SQL_BIT: + pgType = ci->drivers.bools_as_char ? PG_TYPE_CHAR : PG_TYPE_BOOL; + break; + +#if (ODBCVER >= 0x0300) + case SQL_TYPE_DATE: +#endif /* ODBCVER */ + case SQL_DATE: + pgType = PG_TYPE_DATE; + break; + + case SQL_DOUBLE: + case SQL_FLOAT: + pgType = PG_TYPE_FLOAT8; + break; + + case SQL_DECIMAL: + case SQL_NUMERIC: + pgType = PG_TYPE_NUMERIC; + break; + + case SQL_BIGINT: + pgType = PG_TYPE_INT8; + break; + + case SQL_INTEGER: + pgType = PG_TYPE_INT4; + break; + + case SQL_LONGVARBINARY: + if (ci->bytea_as_longvarbinary) + pgType = PG_TYPE_BYTEA; + else + pgType = conn->lobj_type; + break; + + case SQL_LONGVARCHAR: + pgType = ci->drivers.text_as_longvarchar ? PG_TYPE_TEXT : PG_TYPE_VARCHAR; + break; + +#ifdef UNICODE_SUPPORT + case SQL_WLONGVARCHAR: + pgType = ci->drivers.text_as_longvarchar ? PG_TYPE_TEXT : PG_TYPE_VARCHAR; + break; +#endif /* UNICODE_SUPPORT */ + + case SQL_REAL: + pgType = PG_TYPE_FLOAT4; + break; + + case SQL_SMALLINT: + case SQL_TINYINT: + pgType = PG_TYPE_INT2; + break; + + case SQL_TIME: +#if (ODBCVER >= 0x0300) + case SQL_TYPE_TIME: +#endif /* ODBCVER */ + pgType = PG_TYPE_TIME; + break; + + case SQL_TIMESTAMP: +#if (ODBCVER >= 0x0300) + case SQL_TYPE_TIMESTAMP: +#endif /* ODBCVER */ + pgType = PG_TYPE_DATETIME; + break; + + case SQL_VARBINARY: + pgType = PG_TYPE_BYTEA; + break; + + case SQL_VARCHAR: + pgType = PG_TYPE_VARCHAR; + break; + +#if UNICODE_SUPPORT + case SQL_WVARCHAR: + pgType = PG_TYPE_VARCHAR; + break; +#endif /* UNICODE_SUPPORT */ + +#if (ODBCVER >= 0x0350) + case SQL_GUID: + if (PG_VERSION_GE(conn, 8.3)) + pgType = PG_TYPE_UUID; + break; +#endif /* ODBCVER */ - default: - /* hack until permanent type is available */ - if (type == conn->lobj_type) - return SQL_C_BINARY; + case SQL_INTERVAL_MONTH: + case SQL_INTERVAL_YEAR: + case SQL_INTERVAL_YEAR_TO_MONTH: + case SQL_INTERVAL_DAY: + case SQL_INTERVAL_HOUR: + case SQL_INTERVAL_MINUTE: + case SQL_INTERVAL_SECOND: + case SQL_INTERVAL_DAY_TO_HOUR: + case SQL_INTERVAL_DAY_TO_MINUTE: + case SQL_INTERVAL_DAY_TO_SECOND: + case SQL_INTERVAL_HOUR_TO_MINUTE: + case SQL_INTERVAL_HOUR_TO_SECOND: + case SQL_INTERVAL_MINUTE_TO_SECOND: + pgType = PG_TYPE_INTERVAL; + break; + } - /* Experimental, Does this work ? */ -#ifdef EXPERIMENTAL_CURRENTLY - if (ALLOW_WCHAR(conn)) - return SQL_C_WCHAR; -#endif /* EXPERIMENTAL_CURRENTLY */ - return SQL_C_CHAR; + return pgType; +} + +static int +getAtttypmodEtc(const StatementClass *stmt, int col, int *adtsize_or_longestlen) +{ + int atttypmod = -1; + + if (NULL != adtsize_or_longestlen) + *adtsize_or_longestlen = -1; + if (col >= 0) + { + const QResultClass *res; + + if (res = SC_get_Curres(stmt), NULL != res) + { + atttypmod = QR_get_atttypmod(res, col); + if (NULL != adtsize_or_longestlen) + { + if (stmt->catalog_result) + *adtsize_or_longestlen = QR_get_fieldsize(res, col); + else + *adtsize_or_longestlen = QR_get_display_size(res, col); + } + } } + return atttypmod; } +/* + * There are two ways of calling this function: + * + * 1. When going through the supported PG types (SQLGetTypeInfo) + * + * 2. When taking any type id (SQLColumns, SQLGetData) + * + * The first type will always work because all the types defined are returned here. + * The second type will return a default based on global parameter when it does not + * know. This allows for supporting + * types that are unknown. All other pg routines in here return a suitable default. + */ +SQLSMALLINT +pgtype_to_concise_type(const StatementClass *stmt, OID type, int col) +{ + int atttypmod, adtsize_or_longestlen; + + atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); + return pgtype_attr_to_concise_type(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen); +} -const char * -pgtype_to_name(StatementClass *stmt, OID type, BOOL auto_increment) +SQLSMALLINT +pgtype_to_sqldesctype(const StatementClass *stmt, OID type, int col) { - ConnectionClass *conn = SC_get_conn(stmt); - switch (type) - { - case PG_TYPE_CHAR: - return "char"; - case PG_TYPE_CHAR2: - return "char2"; - case PG_TYPE_CHAR4: - return "char4"; - case PG_TYPE_CHAR8: - return "char8"; - case PG_TYPE_INT8: - return auto_increment ? "bigserial" : "int8"; - case PG_TYPE_NUMERIC: - return "numeric"; - case PG_TYPE_VARCHAR: - return "varchar"; - case PG_TYPE_BPCHAR: - return "char"; - case PG_TYPE_TEXT: - return "text"; - case PG_TYPE_NAME: - return "name"; - case PG_TYPE_INT2: - return "int2"; - case PG_TYPE_OID: - return OID_NAME; - case PG_TYPE_XID: - return "xid"; - case PG_TYPE_INT4: -inolog("pgtype_to_name int4\n"); - return auto_increment ? "serial" : "int4"; - case PG_TYPE_FLOAT4: - return "float4"; - case PG_TYPE_FLOAT8: - return "float8"; - case PG_TYPE_DATE: - return "date"; - case PG_TYPE_TIME: - return "time"; - case PG_TYPE_ABSTIME: - return "abstime"; - case PG_TYPE_DATETIME: - if (PG_VERSION_GT(conn, 7.1)) - return "timestamptz"; - else if (PG_VERSION_LT(conn, 7.0)) - return "datetime"; - else - return "timestamp"; - case PG_TYPE_TIMESTAMP_NO_TMZONE: - return "timestamp without time zone"; - case PG_TYPE_TIMESTAMP: - return "timestamp"; - case PG_TYPE_MONEY: - return "money"; - case PG_TYPE_BOOL: - return "bool"; - case PG_TYPE_BYTEA: - return "bytea"; - case PG_TYPE_XML: - return "xml"; - case PG_TYPE_MACADDR: - return "macaddr"; - case PG_TYPE_INET: - return "inet"; - case PG_TYPE_CIDR: - return "cidr"; - case PG_TYPE_UUID: - return "uuid"; + int atttypmod = getAtttypmodEtc(stmt, col, NULL); - case PG_TYPE_LO_UNDEFINED: - return PG_TYPE_LO_NAME; + return pgtype_attr_to_sqldesctype(SC_get_conn(stmt), type, atttypmod); +} - default: - /* hack until permanent type is available */ - if (type == conn->lobj_type) - return PG_TYPE_LO_NAME; +SQLSMALLINT +pgtype_to_datetime_sub(const StatementClass *stmt, OID type, int col) +{ + int atttypmod = getAtttypmodEtc(stmt, col, NULL); - /* - * "unknown" can actually be used in alter table because it is - * a real PG type! - */ - return "unknown"; - } + return pgtype_attr_to_datetime_sub(SC_get_conn(stmt), type, atttypmod); +} + + +SQLSMALLINT +pgtype_to_ctype(const StatementClass *stmt, OID type, int col) +{ + int atttypmod = getAtttypmodEtc(stmt, col, NULL); + + return pgtype_attr_to_ctype(SC_get_conn(stmt), type, atttypmod); } +const char * +pgtype_to_name(const StatementClass *stmt, OID type, int col, BOOL auto_increment) +{ + int atttypmod = getAtttypmodEtc(stmt, col, NULL); + + return pgtype_attr_to_name(SC_get_conn(stmt), type, atttypmod, auto_increment); +} + +#ifdef NOT_USED static SQLSMALLINT -getNumericDecimalDigits(StatementClass *stmt, OID type, int col) +getNumericDecimalDigits(const StatementClass *stmt, OID type, int col) { Int4 atttypmod = -1, default_decimal_digits = 6; QResultClass *result; @@ -672,7 +1533,7 @@ getNumericDecimalDigits(StatementClass *stmt, OID type, int col) static Int4 /* PostgreSQL restritiction */ -getNumericColumnSize(StatementClass *stmt, OID type, int col) +getNumericColumnSize(const StatementClass *stmt, OID type, int col) { Int4 atttypmod = -1, default_column_size = 28; QResultClass *result; @@ -715,9 +1576,8 @@ getNumericColumnSize(StatementClass *stmt, OID type, int col) } } - Int4 -getCharColumnSize(StatementClass *stmt, OID type, int col, int handle_unknown_size_as) +getCharColumnSize(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as) { CSTR func = "getCharColumnSize"; int p = -1, attlen = -1, adtsize = -1, maxsize; @@ -836,227 +1696,97 @@ getCharColumnSize(StatementClass *stmt, OID type, int col, int handle_unknown_si return maxsize; } -static SQLSMALLINT -getTimestampDecimalDigits(StatementClass *stmt, OID type, int col) -{ - ConnectionClass *conn = SC_get_conn(stmt); - Int4 atttypmod; - QResultClass *result; - - mylog("getTimestampDecimalDigits: type=%d, col=%d\n", type, col); - - if (col < 0) - return 0; - if (PG_VERSION_LT(conn, 7.2)) - return 0; - - result = SC_get_Curres(stmt); - - atttypmod = QR_get_atttypmod(result, col); - mylog("atttypmod2=%d\n", atttypmod); - return (atttypmod > -1 ? atttypmod : 6); -} - - -static SQLSMALLINT -getTimestampColumnSize(StatementClass *stmt, OID type, int col) -{ - Int4 fixed, - scale; - - mylog("getTimestampColumnSize: 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: - if (USE_ZONE) - fixed = 22; - else - fixed = 19; - break; - } - scale = getTimestampDecimalDigits(stmt, type, col); - return (scale > 0) ? fixed + 1 + scale : fixed; -} - -/* - * This corresponds to "precision" in ODBC 2.x. - * - * For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, PG_TYPE_NUMERIC, SQLColumns will - * override this length with the atttypmod length from pg_attribute . - * - * If col >= 0, then will attempt to get the info from the result set. - * This is used for functions SQLDescribeCol and SQLColAttributes. - */ -Int4 /* PostgreSQL restriction */ -pgtype_column_size(StatementClass *stmt, OID type, int col, int handle_unknown_size_as) -{ - ConnectionClass *conn = SC_get_conn(stmt); - ConnInfo *ci = &(conn->connInfo); - - if (handle_unknown_size_as == UNKNOWNS_AS_DEFAULT) - handle_unknown_size_as = ci->drivers.unknown_sizes; - switch (type) - { - case PG_TYPE_CHAR: - return 1; - case PG_TYPE_CHAR2: - return 2; - case PG_TYPE_CHAR4: - return 4; - case PG_TYPE_CHAR8: - return 8; - - case PG_TYPE_NAME: - { - int value = 0; - if (PG_VERSION_GT(conn, 7.4)) - value = CC_get_max_idlen(conn); -#ifdef NAME_FIELD_SIZE - else - value = NAME_FIELD_SIZE; -#endif /* NAME_FIELD_SIZE */ - if (0 == value) - { - if (PG_VERSION_GE(conn, 7.3)) - value = NAMEDATALEN_V73; - else - value = NAMEDATALEN_V72; - } - return value; - } - - case PG_TYPE_INT2: - return 5; - - case PG_TYPE_OID: - case PG_TYPE_XID: - case PG_TYPE_INT4: - return 10; - - case PG_TYPE_INT8: - return 19; /* signed */ - - case PG_TYPE_NUMERIC: - return getNumericColumnSize(stmt, type, col); - - case PG_TYPE_FLOAT4: - case PG_TYPE_MONEY: - return 7; - - case PG_TYPE_FLOAT8: - return 15; +static SQLSMALLINT +getTimestampDecimalDigits(const StatementClass *stmt, OID type, int col) +{ + ConnectionClass *conn = SC_get_conn(stmt); + Int4 atttypmod; + QResultClass *result; - case PG_TYPE_DATE: - return 10; - case PG_TYPE_TIME: - return 8; + mylog("getTimestampDecimalDigits: type=%d, col=%d\n", type, col); - case PG_TYPE_ABSTIME: - case PG_TYPE_TIMESTAMP: - return 22; - case PG_TYPE_DATETIME: - case PG_TYPE_TIMESTAMP_NO_TMZONE: - /* return 22; */ - return getTimestampColumnSize(stmt, type, col); + if (col < 0) + return 0; + if (PG_VERSION_LT(conn, 7.2)) + return 0; - case PG_TYPE_BOOL: - return ci->drivers.bools_as_char ? PG_WIDTH_OF_BOOLS_AS_CHAR : 1; + result = SC_get_Curres(stmt); - case PG_TYPE_MACADDR: - return 17; + atttypmod = QR_get_atttypmod(result, col); + mylog("atttypmod2=%d\n", atttypmod); + return (atttypmod > -1 ? atttypmod : 6); +} - case PG_TYPE_INET: - case PG_TYPE_CIDR: - return sizeof("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255/128"); - case PG_TYPE_UUID: - return sizeof("XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX"); - case PG_TYPE_LO_UNDEFINED: - return SQL_NO_TOTAL; +static SQLSMALLINT +getTimestampColumnSize(const StatementClass *stmt, OID type, int col) +{ + Int4 fixed, + scale; + + mylog("getTimestampColumnSize: 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: + if (USE_ZONE) + fixed = 22; + else + fixed = 19; + break; + } + scale = getTimestampDecimalDigits(stmt, type, col); + return (scale > 0) ? fixed + 1 + scale : fixed; +} +#endif /* NOT_USED */ - if (type == conn->lobj_type) /* hack until permanent - * type is available */ - return SQL_NO_TOTAL; - if (PG_TYPE_BYTEA == type && ci->bytea_as_longvarbinary) - return SQL_NO_TOTAL; +/* + * This corresponds to "precision" in ODBC 2.x. + * + * For PG_TYPE_VARCHAR, PG_TYPE_BPCHAR, PG_TYPE_NUMERIC, SQLColumns will + * override this length with the atttypmod length from pg_attribute . + * + * If col >= 0, then will attempt to get the info from the result set. + * This is used for functions SQLDescribeCol and SQLColAttributes. + */ +Int4 /* PostgreSQL restriction */ +pgtype_column_size(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as) +{ + int atttypmod, adtsize_or_longestlen; - /* Handle Character types and unknown types */ - return getCharColumnSize(stmt, type, col, handle_unknown_size_as); - } + atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); + return pgtype_attr_column_size(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, stmt->catalog_result ? UNKNOWNS_AS_CATALOG : handle_unknown_size_as); } /* * precision in ODBC 3.x. */ SQLSMALLINT -pgtype_precision(StatementClass *stmt, OID type, int col, int handle_unknown_size_as) +pgtype_precision(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as) { - switch (type) - { - case PG_TYPE_NUMERIC: - return getNumericColumnSize(stmt, type, col); - case PG_TYPE_DATETIME: - case PG_TYPE_TIMESTAMP_NO_TMZONE: - return getTimestampDecimalDigits(stmt, type, col); - } - return -1; + int atttypmod, adtsize_or_longestlen; + + atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); + return pgtype_attr_precision(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, stmt->catalog_result ? UNKNOWNS_AS_CATALOG : handle_unknown_size_as); } Int4 -pgtype_display_size(StatementClass *stmt, OID type, int col, int handle_unknown_size_as) +pgtype_display_size(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as) { - int dsize; - - switch (type) - { - case PG_TYPE_INT2: - return 6; + int atttypmod, adtsize_or_longestlen; - case PG_TYPE_OID: - case PG_TYPE_XID: - return 10; - - case PG_TYPE_INT4: - return 11; - - case PG_TYPE_INT8: - return 20; /* signed: 19 digits + sign */ - - case PG_TYPE_NUMERIC: - dsize = getNumericColumnSize(stmt, type, col); - return dsize < 0 ? dsize : dsize + 2; - - case PG_TYPE_MONEY: - return 15; /* ($9,999,999.99) */ - - case PG_TYPE_FLOAT4: - return 13; - - case PG_TYPE_FLOAT8: - return 22; - -#if (ODBCVER >= 0x0350) - case PG_TYPE_UUID: - return 36; -#endif /* ODBCVER */ - - /* Character types use regular precision */ - default: - return pgtype_column_size(stmt, type, col, handle_unknown_size_as); - } + atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); + return pgtype_attr_display_size(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, stmt->catalog_result ? UNKNOWNS_AS_CATALOG : handle_unknown_size_as); } @@ -1065,125 +1795,31 @@ pgtype_display_size(StatementClass *stmt, OID type, int col, int handle_unknown_ * or SQLFetchScroll operation if SQL_C_DEFAULT is specified. */ Int4 -pgtype_buffer_length(StatementClass *stmt, OID type, int col, int handle_unknown_size_as) +pgtype_buffer_length(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as) { - ConnectionClass *conn = SC_get_conn(stmt); - - switch (type) - { - case PG_TYPE_INT2: - return 2; /* sizeof(SQLSMALLINT) */ - - case PG_TYPE_OID: - case PG_TYPE_XID: - case PG_TYPE_INT4: - return 4; /* sizeof(SQLINTEGER) */ - - case PG_TYPE_INT8: - if (SQL_C_CHAR == pgtype_to_ctype(stmt, type)) - return 20; /* signed: 19 digits + sign */ - return 8; /* sizeof(SQLSBININT) */ - - case PG_TYPE_NUMERIC: - return getNumericColumnSize(stmt, type, col) + 2; - - case PG_TYPE_FLOAT4: - case PG_TYPE_MONEY: - return 4; /* sizeof(SQLREAL) */ - - case PG_TYPE_FLOAT8: - return 8; /* sizeof(SQLFLOAT) */ - - case PG_TYPE_DATE: - case PG_TYPE_TIME: - return 6; /* sizeof(DATE(TIME)_STRUCT) */ - - case PG_TYPE_ABSTIME: - case PG_TYPE_DATETIME: - case PG_TYPE_TIMESTAMP: - case PG_TYPE_TIMESTAMP_NO_TMZONE: - return 16; /* sizeof(TIMESTAMP_STRUCT) */ + int atttypmod, adtsize_or_longestlen; - case PG_TYPE_UUID: - return 16; /* sizeof(SQLGUID) */ - - /* Character types use the default precision */ - case PG_TYPE_VARCHAR: - case PG_TYPE_BPCHAR: - { - int coef = 1; - Int4 prec = pgtype_column_size(stmt, type, col, handle_unknown_size_as), maxvarc; - if (SQL_NO_TOTAL == prec) - return prec; -#ifdef UNICODE_SUPPORT - if (CC_is_in_unicode_driver(conn)) - return prec * WCLEN; -#endif /* UNICODE_SUPPORT */ - /* after 7.2 */ - if (PG_VERSION_GE(conn, 7.2)) - coef = conn->mb_maxbyte_per_char; - if (coef < 2 && (conn->connInfo).lf_conversion) - /* CR -> CR/LF */ - coef = 2; - if (coef == 1) - return prec; - maxvarc = conn->connInfo.drivers.max_varchar_size; - if (prec <= maxvarc && prec * coef > maxvarc) - return maxvarc; - return coef * prec; - } - default: - return pgtype_column_size(stmt, type, col, handle_unknown_size_as); - } + atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); + return pgtype_attr_buffer_length(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, stmt->catalog_result ? UNKNOWNS_AS_CATALOG : handle_unknown_size_as); } /* */ Int4 -pgtype_desclength(StatementClass *stmt, OID type, int col, int handle_unknown_size_as) +pgtype_desclength(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as) { - switch (type) - { - case PG_TYPE_INT2: - return 2; - - case PG_TYPE_OID: - case PG_TYPE_XID: - case PG_TYPE_INT4: - return 4; - - case PG_TYPE_INT8: - return 20; /* signed: 19 digits + sign */ - - case PG_TYPE_NUMERIC: - return getNumericColumnSize(stmt, type, col) + 2; - - case PG_TYPE_FLOAT4: - case PG_TYPE_MONEY: - return 4; - - case PG_TYPE_FLOAT8: - return 8; + int atttypmod, adtsize_or_longestlen; - case PG_TYPE_DATE: - case PG_TYPE_TIME: - case PG_TYPE_ABSTIME: - case PG_TYPE_DATETIME: - case PG_TYPE_TIMESTAMP_NO_TMZONE: - case PG_TYPE_TIMESTAMP: - case PG_TYPE_VARCHAR: - case PG_TYPE_BPCHAR: - return pgtype_column_size(stmt, type, col, handle_unknown_size_as); - default: - return pgtype_column_size(stmt, type, col, handle_unknown_size_as); - } + atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); + return pgtype_attr_desclength(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, stmt->catalog_result ? UNKNOWNS_AS_CATALOG : handle_unknown_size_as); } +#ifdef NOT_USED /* * Transfer octet length. */ Int4 -pgtype_transfer_octet_length(StatementClass *stmt, OID type, int column_size) +pgtype_transfer_octet_length(const StatementClass *stmt, OID type, int column_size) { ConnectionClass *conn = SC_get_conn(stmt); @@ -1220,12 +1856,13 @@ pgtype_transfer_octet_length(StatementClass *stmt, OID type, int column_size) } return -1; } +#endif /* NOT_USED */ /* * corrsponds to "min_scale" in ODBC 2.x. */ Int2 -pgtype_min_decimal_digits(StatementClass *stmt, OID type) +pgtype_min_decimal_digits(const ConnectionClass *conn, OID type) { switch (type) { @@ -1253,7 +1890,7 @@ pgtype_min_decimal_digits(StatementClass *stmt, OID type) * corrsponds to "max_scale" in ODBC 2.x. */ Int2 -pgtype_max_decimal_digits(StatementClass *stmt, OID type) +pgtype_max_decimal_digits(const ConnectionClass *conn, OID type) { switch (type) { @@ -1273,7 +1910,7 @@ pgtype_max_decimal_digits(StatementClass *stmt, OID type) case PG_TYPE_TIMESTAMP_NO_TMZONE: return 38; case PG_TYPE_NUMERIC: - return getNumericDecimalDigits(stmt, type, -1); + return getNumericDecimalDigitsX(conn, type, -1, -1, UNKNOWNS_AS_DEFAULT); default: return -1; } @@ -1283,57 +1920,29 @@ pgtype_max_decimal_digits(StatementClass *stmt, OID type) * corrsponds to "scale" in ODBC 2.x. */ Int2 -pgtype_decimal_digits(StatementClass *stmt, OID type, int col) +pgtype_decimal_digits(const StatementClass *stmt, OID type, int col) { - switch (type) - { - case PG_TYPE_INT2: - case PG_TYPE_OID: - case PG_TYPE_XID: - case PG_TYPE_INT4: - case PG_TYPE_INT8: - case PG_TYPE_FLOAT4: - case PG_TYPE_FLOAT8: - case PG_TYPE_MONEY: - case PG_TYPE_BOOL: - - /* - * Number of digits to the right of the decimal point in - * "yyyy-mm=dd hh:mm:ss[.f...]" - */ - case PG_TYPE_ABSTIME: - case PG_TYPE_TIMESTAMP: - return 0; - case PG_TYPE_DATETIME: - case PG_TYPE_TIMESTAMP_NO_TMZONE: - /* return 0; */ - return getTimestampDecimalDigits(stmt, type, col); - - case PG_TYPE_NUMERIC: - return getNumericDecimalDigits(stmt, type, col); + int atttypmod, adtsize_or_longestlen; - default: - return -1; - } + atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); + return pgtype_attr_decimal_digits(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, stmt->catalog_result ? UNKNOWNS_AS_CATALOG : UNKNOWNS_AS_DEFAULT); } /* * "scale" in ODBC 3.x. */ Int2 -pgtype_scale(StatementClass *stmt, OID type, int col) +pgtype_scale(const StatementClass *stmt, OID type, int col) { - switch (type) - { - case PG_TYPE_NUMERIC: - return getNumericDecimalDigits(stmt, type, col); - } - return -1; + int atttypmod, adtsize_or_longestlen; + + atttypmod = getAtttypmodEtc(stmt, col, &adtsize_or_longestlen); + return pgtype_attr_scale(SC_get_conn(stmt), type, atttypmod, adtsize_or_longestlen, stmt->catalog_result ? UNKNOWNS_AS_CATALOG : UNKNOWNS_AS_DEFAULT); } Int2 -pgtype_radix(StatementClass *stmt, OID type) +pgtype_radix(const ConnectionClass *conn, OID type) { switch (type) { @@ -1354,14 +1963,14 @@ pgtype_radix(StatementClass *stmt, OID type) Int2 -pgtype_nullable(StatementClass *stmt, OID type) +pgtype_nullable(const ConnectionClass *conn, OID type) { return SQL_NULLABLE; /* everything should be nullable */ } Int2 -pgtype_auto_increment(StatementClass *stmt, OID type) +pgtype_auto_increment(const ConnectionClass *conn, OID type) { switch (type) { @@ -1392,7 +2001,7 @@ pgtype_auto_increment(StatementClass *stmt, OID type) Int2 -pgtype_case_sensitive(StatementClass *stmt, OID type) +pgtype_case_sensitive(const ConnectionClass *conn, OID type) { switch (type) { @@ -1406,6 +2015,7 @@ pgtype_case_sensitive(StatementClass *stmt, OID type) case PG_TYPE_BPCHAR: case PG_TYPE_TEXT: case PG_TYPE_NAME: + case PG_TYPE_REFCURSOR: return TRUE; default: @@ -1415,7 +2025,7 @@ pgtype_case_sensitive(StatementClass *stmt, OID type) Int2 -pgtype_money(StatementClass *stmt, OID type) +pgtype_money(const ConnectionClass *conn, OID type) { switch (type) { @@ -1428,7 +2038,7 @@ pgtype_money(StatementClass *stmt, OID type) Int2 -pgtype_searchable(StatementClass *stmt, OID type) +pgtype_searchable(const ConnectionClass *conn, OID type) { switch (type) { @@ -1441,10 +2051,11 @@ pgtype_searchable(StatementClass *stmt, OID type) case PG_TYPE_BPCHAR: case PG_TYPE_TEXT: case PG_TYPE_NAME: + case PG_TYPE_REFCURSOR: return SQL_SEARCHABLE; default: - if (stmt && type == SC_get_conn(stmt)->lobj_type) + if (conn && type == conn->lobj_type) return SQL_UNSEARCHABLE; return SQL_ALL_EXCEPT_LIKE; } @@ -1452,7 +2063,7 @@ pgtype_searchable(StatementClass *stmt, OID type) Int2 -pgtype_unsigned(StatementClass *stmt, OID type) +pgtype_unsigned(const ConnectionClass *conn, OID type) { switch (type) { @@ -1476,7 +2087,7 @@ pgtype_unsigned(StatementClass *stmt, OID type) const char * -pgtype_literal_prefix(StatementClass *stmt, OID type) +pgtype_literal_prefix(const ConnectionClass *conn, OID type) { switch (type) { @@ -1498,7 +2109,7 @@ pgtype_literal_prefix(StatementClass *stmt, OID type) const char * -pgtype_literal_suffix(StatementClass *stmt, OID type) +pgtype_literal_suffix(const ConnectionClass *conn, OID type) { switch (type) { @@ -1520,7 +2131,7 @@ pgtype_literal_suffix(StatementClass *stmt, OID type) const char * -pgtype_create_params(StatementClass *stmt, OID type) +pgtype_create_params(const ConnectionClass *conn, OID type) { switch (type) { @@ -1682,6 +2293,22 @@ ctype_length(SQLSMALLINT ctype) case SQL_C_GUID: return sizeof(SQLGUID); #endif /* ODBCVER */ +#if (ODBCVER >= 0x0300) + case SQL_C_INTERVAL_YEAR: + case SQL_C_INTERVAL_MONTH: + case SQL_C_INTERVAL_YEAR_TO_MONTH: + case SQL_C_INTERVAL_DAY: + case SQL_C_INTERVAL_HOUR: + case SQL_C_INTERVAL_DAY_TO_HOUR: + case SQL_C_INTERVAL_MINUTE: + case SQL_C_INTERVAL_DAY_TO_MINUTE: + case SQL_C_INTERVAL_HOUR_TO_MINUTE: + case SQL_C_INTERVAL_SECOND: + case SQL_C_INTERVAL_DAY_TO_SECOND: + case SQL_C_INTERVAL_HOUR_TO_SECOND: + case SQL_C_INTERVAL_MINUTE_TO_SECOND: + return sizeof(SQL_INTERVAL_STRUCT); +#endif /* ODBCVER */ case SQL_C_BINARY: case SQL_C_CHAR: diff --git a/pgtypes.h b/pgtypes.h index f81ed20..dae2573 100644 --- a/pgtypes.h +++ b/pgtypes.h @@ -69,6 +69,7 @@ #define PG_TYPE_TIME 1083 #define PG_TYPE_TIMESTAMP_NO_TMZONE 1114 /* since 7.2 */ #define PG_TYPE_DATETIME 1184 +#define PG_TYPE_INTERVAL 1186 #define PG_TYPE_TIME_WITH_TMZONE 1266 /* since 7.1 */ #define PG_TYPE_TIMESTAMP 1296 /* deprecated since 7.0 */ #define PG_TYPE_NUMERIC 1700 @@ -83,40 +84,61 @@ extern SQLSMALLINT sqlTypes[]; /* Defines for pgtype_precision */ #define PG_STATIC (-1) +#define PG_UNSPECIFIED (-1) #define PG_WIDTH_OF_BOOLS_AS_CHAR 5 +#if (ODBCVER >= 0x0300) +/* + * SQL_INTERVAL support is disabled because I found + * some applications which are unhappy with it. + * +#define PG_INTERVAL_AS_SQL_INTERVAL + */ +#endif /* ODBCVER */ + OID pg_true_type(const ConnectionClass *, OID, OID); -OID sqltype_to_pgtype(StatementClass *stmt, SQLSMALLINT fSqlType); +OID sqltype_to_pgtype(const ConnectionClass *conn, SQLSMALLINT fSqlType); -SQLSMALLINT pgtype_to_concise_type(StatementClass *stmt, OID type, int col); -SQLSMALLINT pgtype_to_sqldesctype(StatementClass *stmt, OID type, int col); -SQLSMALLINT pgtype_to_datetime_sub(StatementClass *stmt, OID type); -SQLSMALLINT pgtype_to_ctype(StatementClass *stmt, OID type); -const char *pgtype_to_name(StatementClass *stmt, OID type, BOOL auto_increment); +SQLSMALLINT pgtype_to_concise_type(const StatementClass *stmt, OID type, int col); +SQLSMALLINT pgtype_to_sqldesctype(const StatementClass *stmt, OID type, int col); +SQLSMALLINT pgtype_to_datetime_sub(const StatementClass *stmt, OID type, int col); +SQLSMALLINT pgtype_to_ctype(const StatementClass *stmt, OID type, int col); +const char *pgtype_to_name(const StatementClass *stmt, OID type, int col, BOOL auto_increment); + +SQLSMALLINT pgtype_attr_to_concise_type(const ConnectionClass *conn, OID type, int typmod, int adtsize_or_longestlen); +SQLSMALLINT pgtype_attr_to_sqldesctype(const ConnectionClass *conn, OID type, int typmod); +SQLSMALLINT pgtype_attr_to_datetime_sub(const ConnectionClass *conn, OID type, int typmod); +SQLSMALLINT pgtype_attr_to_ctype(const ConnectionClass *conn, OID type, int typmod); +const char *pgtype_attr_to_name(const ConnectionClass *conn, OID type, int typmod, BOOL auto_increment); +Int4 pgtype_attr_column_size(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longest, int handle_unknown_size_as); +Int4 pgtype_attr_buffer_length(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as); +Int4 pgtype_attr_display_size(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as); +Int2 pgtype_attr_decimal_digits(const ConnectionClass *conn, OID type, int atttypmod, int adtsize_or_longestlen, int handle_unknown_size_as); +Int4 pgtype_attr_transfer_octet_length(const ConnectionClass *conn, OID type, int atttypmod, int handle_unknown_size_as); /* These functions can use static numbers or result sets(col parameter) */ -Int4 pgtype_column_size(StatementClass *stmt, OID type, int col, int handle_unknown_size_as); /* corresponds to "precision" in ODBC 2.x */ -SQLSMALLINT pgtype_precision(StatementClass *stmt, OID type, int col, int handle_unknown_size_as); /* "precsion in ODBC 3.x */ +Int4 pgtype_column_size(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as); /* corresponds to "precision" in ODBC 2.x */ +SQLSMALLINT pgtype_precision(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as); /* "precsion in ODBC 3.x */ /* the following size/length are of Int4 due to PG restriction */ -Int4 pgtype_display_size(StatementClass *stmt, OID type, int col, int handle_unknown_size_as); -Int4 pgtype_buffer_length(StatementClass *stmt, OID type, int col, int handle_unknown_size_as); -Int4 pgtype_desclength(StatementClass *stmt, OID type, int col, int handle_unknown_size_as); -Int4 pgtype_transfer_octet_length(StatementClass *stmt, OID type, int column_size); - -SQLSMALLINT pgtype_decimal_digits(StatementClass *stmt, OID type, int col); /* corresponds to "scale" in ODBC 2.x */ -SQLSMALLINT pgtype_min_decimal_digits(StatementClass *stmt, OID type); /* corresponds to "min_scale" in ODBC 2.x */ -SQLSMALLINT pgtype_max_decimal_digits(StatementClass *stmt, OID type); /* corresponds to "max_scale" in ODBC 2.x */ -SQLSMALLINT pgtype_scale(StatementClass *stmt, OID type, int col); /* ODBC 3.x " */ -Int2 pgtype_radix(StatementClass *stmt, OID type); -Int2 pgtype_nullable(StatementClass *stmt, OID type); -Int2 pgtype_auto_increment(StatementClass *stmt, OID type); -Int2 pgtype_case_sensitive(StatementClass *stmt, OID type); -Int2 pgtype_money(StatementClass *stmt, OID type); -Int2 pgtype_searchable(StatementClass *stmt, OID type); -Int2 pgtype_unsigned(StatementClass *stmt, OID type); -const char *pgtype_literal_prefix(StatementClass *stmt, OID type); -const char *pgtype_literal_suffix(StatementClass *stmt, OID type); -const char *pgtype_create_params(StatementClass *stmt, OID type); +Int4 pgtype_display_size(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as); +Int4 pgtype_buffer_length(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as); +Int4 pgtype_desclength(const StatementClass *stmt, OID type, int col, int handle_unknown_size_as); +// Int4 pgtype_transfer_octet_length(const ConnectionClass *conn, OID type, int column_size); + +SQLSMALLINT pgtype_decimal_digits(const StatementClass *stmt, OID type, int col); /* corresponds to "scale" in ODBC 2.x */ +SQLSMALLINT pgtype_min_decimal_digits(const ConnectionClass *conn, OID type); /* corresponds to "min_scale" in ODBC 2.x */ +SQLSMALLINT pgtype_max_decimal_digits(const ConnectionClass *conn, OID type); /* corresponds to "max_scale" in ODBC 2.x */ +SQLSMALLINT pgtype_scale(const StatementClass *stmt, OID type, int col); /* ODBC 3.x " */ +Int2 pgtype_radix(const ConnectionClass *conn, OID type); +Int2 pgtype_nullable(const ConnectionClass *conn, OID type); +Int2 pgtype_auto_increment(const ConnectionClass *conn, OID type); +Int2 pgtype_case_sensitive(const ConnectionClass *conn, OID type); +Int2 pgtype_money(const ConnectionClass *conn, OID type); +Int2 pgtype_searchable(const ConnectionClass *conn, OID type); +Int2 pgtype_unsigned(const ConnectionClass *conn, OID type); +const char *pgtype_literal_prefix(const ConnectionClass *conn, OID type); +const char *pgtype_literal_suffix(const ConnectionClass *conn, OID type); +const char *pgtype_create_params(const ConnectionClass *conn, OID type); SQLSMALLINT sqltype_to_default_ctype(const ConnectionClass *stmt, SQLSMALLINT sqltype); Int4 ctype_length(SQLSMALLINT ctype); diff --git a/qresult.c b/qresult.c index 01c24db..6aaf8d3 100644 --- a/qresult.c +++ b/qresult.c @@ -634,9 +634,13 @@ QR_close(QResultClass *self) } else { - UDWORD flag = ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN; + BOOL does_commit = FALSE; + UDWORD flag = 0; char buf[64]; + if (QR_needs_survival_check(self)) + flag = ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN; + snprintf(buf, sizeof(buf), "close \"%s\"", QR_get_cursor(self)); /* End the transaction if there are no cursors left on this conn */ if (CC_is_in_trans(conn) && @@ -644,13 +648,27 @@ QR_close(QResultClass *self) CC_cursor_count(conn) <= 1) { mylog("QResult: END transaction on conn=%p\n", conn); - strlcat(buf, ";commit", sizeof(buf)); - flag |= END_WITH_COMMIT; - QR_set_cursor(self, NULL); + if ((ROLLBACK_ON_ERROR & flag) == 0) + { + strlcat(buf, ";commit", sizeof(buf)); + flag |= END_WITH_COMMIT; + QR_set_cursor(self, NULL); + } + else + does_commit = TRUE; } res = CC_send_query(conn, buf, NULL, flag, NULL); QR_Destructor(res); + if (does_commit) + { + if (!CC_commit(conn)) + { + QR_set_rstatus(self, PORES_FATAL_ERROR); + QR_set_message(self, "Error ending transaction on autocommit."); + ret = FALSE; + } + } } QR_set_no_fetching_tuples(self); diff --git a/qresult.h b/qresult.h index 216db20..6af353c 100644 --- a/qresult.h +++ b/qresult.h @@ -46,6 +46,7 @@ enum FQR_FETCHING_TUPLES = 1L /* is fetching tuples from db */ ,FQR_REACHED_EOF = (1L << 1) /* reached eof */ ,FQR_HAS_VALID_BASE = (1L << 2) + ,FQR_NEEDS_SURVIVAL_CHECK = (1L << 3) /* check if the cursor is open */ }; struct QResultClass_ @@ -158,7 +159,7 @@ enum { #define QR_set_aborted(self, aborted_) ( self->aborted = aborted_) #define QR_set_haskeyset(self) (self->flags |= FQR_HASKEYSET) #define QR_set_synchronize_keys(self) (self->flags |= FQR_SYNCHRONIZEKEYS) -#define QR_set_no_cursor(self) (self->flags &= ~(FQR_WITHHOLD | FQR_HOLDPERMANENT)) +#define QR_set_no_cursor(self) (self->flags &= ~(FQR_WITHHOLD | FQR_HOLDPERMANENT | FQR_NEEDS_SURVIVAL_CHECK)) #define QR_set_withhold(self) (self->flags |= FQR_WITHHOLD) #define QR_set_permanent(self) (self->flags |= FQR_HOLDPERMANENT) #define QR_set_reached_eof(self) (self->pstatus |= FQR_REACHED_EOF) @@ -166,6 +167,8 @@ enum { #define QR_set_no_fetching_tuples(self) (self->pstatus &= ~FQR_FETCHING_TUPLES) #define QR_set_has_valid_base(self) (self->pstatus |= FQR_HAS_VALID_BASE) #define QR_set_no_valid_base(self) (self->pstatus &= ~FQR_HAS_VALID_BASE) +#define QR_set_survival_check(self) (self->pstatus |= FQR_NEEDS_SURVIVAL_CHECK) +#define QR_set_no_survival_check(self) (self->pstatus &= ~FQR_NEEDS_SURVIVAL_CHECK) #define QR_inc_num_cache(self) \ do { \ self->num_cached_rows++; \ @@ -195,6 +198,7 @@ do { \ #define QR_once_reached_eof(self) ((self->pstatus & FQR_REACHED_EOF) != 0) #define QR_is_fetching_tuples(self) ((self->pstatus & FQR_FETCHING_TUPLES) != 0) #define QR_has_valid_base(self) (0 != (self->pstatus & FQR_HAS_VALID_BASE)) +#define QR_needs_survival_check(self) (0 != (self->pstatus & FQR_NEEDS_SURVIVAL_CHECK)) #define QR_aborted(self) (!self || self->aborted) #define QR_get_reqsize(self) (self->rowset_size_include_ommitted) diff --git a/results.c b/results.c index 2b72585..12a772b 100644 --- a/results.c +++ b/results.c @@ -429,7 +429,7 @@ inolog("answering bookmark info\n"); if (SC_has_outer_join(stmt)) *pfNullable = TRUE; else - *pfNullable = fi ? fi->nullable : pgtype_nullable(stmt, fieldtype); + *pfNullable = fi ? fi->nullable : pgtype_nullable(conn, fieldtype); mylog("describeCol: col %d *pfNullable = %d\n", icol, *pfNullable); } @@ -521,7 +521,9 @@ inolog("answering bookmark info\n"); unknown_sizes = ci->drivers.unknown_sizes; /* not appropriate for SQLColAttributes() */ - if (unknown_sizes == UNKNOWNS_AS_DONTKNOW) + if (stmt->catalog_result) + unknown_sizes = UNKNOWNS_AS_CATALOG; + else if (unknown_sizes == UNKNOWNS_AS_DONTKNOW) unknown_sizes = UNKNOWNS_AS_MAX; if (!stmt->catalog_result && SC_is_parse_forced(stmt) && stmt->statement_type == STMT_TYPE_SELECT) @@ -638,7 +640,7 @@ inolog("answering bookmark info\n"); if (fi && fi->auto_increment) value = TRUE; else - value = pgtype_auto_increment(stmt, field_type); + value = pgtype_auto_increment(conn, field_type); if (value == -1) /* non-numeric becomes FALSE (ODBC Doc) */ value = FALSE; mylog("AUTO_INCREMENT=%d\n", value); @@ -646,7 +648,7 @@ inolog("answering bookmark info\n"); break; case SQL_COLUMN_CASE_SENSITIVE: /* == SQL_DESC_CASE_SENSITIVE */ - value = pgtype_case_sensitive(stmt, field_type); + value = pgtype_case_sensitive(conn, field_type); break; /* @@ -694,7 +696,7 @@ inolog(" (%s,%s)", PRINT_NAME(fi->column_alias), PRINT_NAME(fi->column_name)); break; case SQL_COLUMN_MONEY: /* == SQL_DESC_FIXED_PREC_SCALE */ - value = pgtype_money(stmt, field_type); + value = pgtype_money(conn, field_type); inolog("COLUMN_MONEY=%d\n", value); break; @@ -706,7 +708,7 @@ inolog("COLUMN_MONEY=%d\n", value); if (SC_has_outer_join(stmt)) value = TRUE; else - value = fi ? fi->nullable : pgtype_nullable(stmt, field_type); + value = fi ? fi->nullable : pgtype_nullable(conn, field_type); inolog("COLUMN_NULLABLE=%d\n", value); break; @@ -735,7 +737,7 @@ inolog("COLUMN_SCALE=%d\n", value); break; case SQL_COLUMN_SEARCHABLE: /* == SQL_DESC_SEARCHABLE */ - value = pgtype_searchable(stmt, field_type); + value = pgtype_searchable(conn, field_type); break; case SQL_COLUMN_TABLE_NAME: /* == SQL_DESC_TABLE_NAME */ @@ -750,11 +752,11 @@ inolog("COLUMN_SCALE=%d\n", value); break; case SQL_COLUMN_TYPE_NAME: /* == SQL_DESC_TYPE_NAME */ - p = pgtype_to_name(stmt, field_type, fi && fi->auto_increment); + p = pgtype_to_name(stmt, field_type, col_idx, fi && fi->auto_increment); break; case SQL_COLUMN_UNSIGNED: /* == SQL_DESC_UNSINGED */ - value = pgtype_unsigned(stmt, field_type); + value = pgtype_unsigned(conn, field_type); if (value == -1) /* non-numeric becomes TRUE (ODBC Doc) */ value = SQL_TRUE; @@ -798,14 +800,15 @@ inolog("COLUMN_SCALE=%d\n", value); mylog("%s: BASE_TABLE_NAME = '%s'\n", func, p); break; case SQL_DESC_LENGTH: /* different from SQL_COLUMN_LENGTH */ - value = (fi && fi->length > 0) ? fi->length : pgtype_desclength(stmt, field_type, col_idx, unknown_sizes); + // value = (fi && fi->length > 0) ? fi->length : pgtype_desclength(stmt, field_type, col_idx, unknown_sizes); + value = (fi && column_size > 0) ? column_size : pgtype_desclength(stmt, field_type, col_idx, unknown_sizes); if (-1 == value) value = 0; mylog("%s: col %d, desc_length = %d\n", func, col_idx, value); break; case SQL_DESC_OCTET_LENGTH: - value = (fi && fi->length > 0) ? fi->length : pgtype_transfer_octet_length(stmt, field_type, column_size); + value = (fi && fi->length > 0) ? fi->length : pgtype_attr_transfer_octet_length(conn, field_type, column_size, unknown_sizes); if (-1 == value) value = 0; mylog("%s: col %d, octet_length = %d\n", func, col_idx, value); @@ -824,19 +827,19 @@ inolog("COLUMN_SCALE=%d\n", value); value = 0; break; case SQL_DESC_LOCAL_TYPE_NAME: - p = pgtype_to_name(stmt, field_type, fi && fi->auto_increment); + p = pgtype_to_name(stmt, field_type, col_idx, fi && fi->auto_increment); break; case SQL_DESC_TYPE: value = pgtype_to_sqldesctype(stmt, field_type, col_idx); break; case SQL_DESC_NUM_PREC_RADIX: - value = pgtype_radix(stmt, field_type); + value = pgtype_radix(conn, field_type); break; case SQL_DESC_LITERAL_PREFIX: - p = pgtype_literal_prefix(stmt, field_type); + p = pgtype_literal_prefix(conn, field_type); break; case SQL_DESC_LITERAL_SUFFIX: - p = pgtype_literal_suffix(stmt, field_type); + p = pgtype_literal_suffix(conn, field_type); break; case SQL_DESC_UNNAMED: value = (fi && NAME_IS_NULL(fi->column_name) && NAME_IS_NULL(fi->column_alias)) ? SQL_UNNAMED : SQL_NAMED; @@ -910,11 +913,13 @@ PGAPI_GetData( UInt2 num_cols; SQLLEN num_rows; OID field_type; + int atttypmod; void *value = NULL; RETCODE result = SQL_SUCCESS; char get_bookmark = FALSE; ConnInfo *ci; SQLSMALLINT target_type; + int precision = -1; mylog("%s: enter, stmt=%p icol=%d\n", func, stmt, icol); @@ -952,6 +957,7 @@ PGAPI_GetData( { target_type = binfo->returntype; mylog("SQL_ARD_TYPE=%d\n", target_type); + precision = binfo->precision; } else { @@ -1067,13 +1073,14 @@ inolog("currT=%d base=%d rowset=%d\n", stmt->currTuple, QR_get_rowstart_in_cache } field_type = QR_get_field_type(res, icol); + atttypmod = QR_get_atttypmod(res, icol); mylog("**** %s: icol = %d, target_type = %d, field_type = %d, value = '%s'\n", func, icol, target_type, field_type, value ? value : "(null)"); SC_set_current_col(stmt, icol); - result = copy_and_convert_field(stmt, field_type, value, - target_type, rgbValue, cbValueMax, pcbValue, pcbValue); + result = copy_and_convert_field(stmt, field_type, atttypmod, value, + target_type, precision, rgbValue, cbValueMax, pcbValue, pcbValue); switch (result) { @@ -3941,8 +3948,10 @@ irow_insert(RETCODE ret, StatementClass *stmt, StatementClass *istmt, SQLLEN add SC_set_current_col(stmt, -1); copy_and_convert_field(stmt, PG_TYPE_INT4, + PG_UNSPECIFIED, buf, bookmark->returntype, + 0, bookmark->buffer + offset, bookmark->buflen, LENADDR_SHIFT(bookmark->used, offset), diff --git a/statement.c b/statement.c index 784c2e0..327b907 100644 --- a/statement.c +++ b/statement.c @@ -1532,13 +1532,14 @@ SC_fetch(StatementClass *self) Int2 num_cols, lf; OID type; + int atttypmod; char *value; ColumnInfoClass *coli; BindInfoClass *bookmark; /* TupleField *tupleField; */ -inolog("%s statement=%p ommitted=0\n", func, self); +inolog("%s statement=%p res=%x ommitted=0\n", func, self, res); self->last_fetch_count = self->last_fetch_count_include_ommitted = 0; if (!res) return SQL_ERROR; @@ -1641,8 +1642,8 @@ inolog("%s: stmt=%p ommitted++\n", func, self); sprintf(buf, FORMAT_ULEN, SC_get_bookmark(self)); SC_set_current_col(self, -1); - result = copy_and_convert_field(self, 0, buf, - SQL_C_ULONG, bookmark->buffer + offset, 0, + result = copy_and_convert_field(self, 0, PG_UNSPECIFIED, buf, + SQL_C_ULONG, 0, bookmark->buffer + offset, 0, LENADDR_SHIFT(bookmark->used, offset), LENADDR_SHIFT(bookmark->used, offset)); } @@ -1670,8 +1671,9 @@ inolog("%s: stmt=%p ommitted++\n", func, self); /* type = QR_get_field_type(res, lf); */ type = CI_get_oid(coli, lf); /* speed things up */ + atttypmod = CI_get_atttypmod(coli, lf); /* speed things up */ - mylog("type = %d\n", type); + mylog("type = %d, atttypmod = %d\n", type, atttypmod); if (SC_is_fetchcursor(self)) value = QR_get_value_backend(res, lf); @@ -1685,7 +1687,7 @@ inolog("curt=%d\n", curt); mylog("value = '%s'\n", (value == NULL) ? "" : value); - retval = copy_and_convert_field_bindinfo(self, type, value, lf); + retval = copy_and_convert_field_bindinfo(self, type, atttypmod, value, lf); mylog("copy_and_convert: retval = %d\n", retval); @@ -2099,6 +2101,34 @@ inolog("res->next=%p\n", tres); { Int2 io, out; has_out_para = (CountParameters(self, NULL, &io, &out) > 0); +/* + * I'm not sure if the following REFCIR_SUPPORT stuff is valuable + * or not. + */ +#ifdef REFCUR_SUPPORT + +inolog("!!! numfield=%d field_type=%u\n", QR_NumResultCols(res), QR_get_field_type(res, 0)); + if (!has_out_para && + 0 < QR_NumResultCols(res) && + PG_TYPE_REFCURSOR == QR_get_field_type(res, 0)) + { + char fetch[128]; + int stmt_type = self->statement_type; + + STR_TO_NAME(self->cursor_name, QR_get_value_backend_text(res, 0, 0)); + QR_Destructor(res); + SC_init_Result(self); + SC_set_fetchcursor(self); + qi.result_in = NULL; + qi.cursor = SC_cursor_name(self); + qi.row_size = ci->drivers.fetch_max; + snprintf(fetch, sizeof(fetch), "%s " FORMAT_LEN " in \"%s\"", fetch_cmd, qi.row_size, SC_cursor_name(self)); + if (0 != (ci->extra_opts & BIT_IGNORE_ROUND_TRIP_TIME)) + qflag |= IGNORE_ROUND_TRIP; + if (res = CC_send_query_append(conn, fetch, &qi, qflag, SC_get_ancestor(self), NULL), NULL != res) + SC_set_Result(self, res); + } +#endif /* REFCUR_SUPPORT */ } if (has_out_para) { /* get the return value of the procedure call */ diff --git a/version.h b/version.h index af8cfe7..bdfec4c 100644 --- a/version.h +++ b/version.h @@ -9,9 +9,9 @@ #ifndef __VERSION_H__ #define __VERSION_H__ -#define POSTGRESDRIVERVERSION "08.04.0201" -#define POSTGRES_RESOURCE_VERSION "08.04.0201\0" -#define PG_DRVFILE_VERSION 8,4,02,01 -#define PG_BUILD_VERSION "2010008210001" +#define POSTGRESDRIVERVERSION "08.04.0202" +#define POSTGRES_RESOURCE_VERSION "08.04.0202\0" +#define PG_DRVFILE_VERSION 8,4,02,02 +#define PG_BUILD_VERSION "201008210001" #endif -- 2.39.5