1. Close (holdable) cursors on commit if possible.
2. Recycle columns cache info if the size becomes pretty large.
3. Add sslverify=none to conninfo in case of SSL connections
via libpq of version 8.4.
4. Add a functionality to change the directory for logging.
5. Correct the error code for communication errors.
#
# Makefile.am for psqlodbc30w (PostgreSQL ODBC driver)
#
-# $Header: /home/heikki/psqlodbc-cvs-copy/psqlodbc/Makefile.am,v 1.51 2008/11/07 14:37:25 h-saito Exp $
+# $Header: /home/heikki/psqlodbc-cvs-copy/psqlodbc/Makefile.am,v 1.52 2009/03/20 15:39:22 hinoue Exp $
#
#-------------------------------------------------------------------------
AM_LDFLAGS = -module -no-undefined -avoid-version
-psqlodbca_la_LIBADD = -lpq
-psqlodbcw_la_LIBADD = -lpq
+psqlodbca_la_LIBADD = -lpq -lssl
+psqlodbcw_la_LIBADD = -lpq -lssl
psqlodbca_la_SOURCES = \
info.c bind.c columninfo.c connection.c convert.c drvconn.c \
AC_MSG_ERROR([pg_config not found (set PG_CONFIG environment variable)])
fi
-pg_includedir=`$PG_CONFIG --includedir`
-pg_libdir=`$PG_CONFIG --libdir`
+pg_includedir=`"$PG_CONFIG" --includedir`
+pg_libdir=`"$PG_CONFIG" --libdir`
CPPFLAGS="$CPPFLAGS -I$pg_includedir"
LDFLAGS="$LDFLAGS -L$pg_libdir"
fi
fi
+# 8. Libltdl
+AC_CHECK_LIB(ltdl, lt_dloepn)
AC_CONFIG_FILES([Makefile])
static void CC_lookup_pg_version(ConnectionClass *self);
static void CC_lookup_lo(ConnectionClass *self);
static char *CC_create_errormsg(ConnectionClass *self);
+static int CC_close_eof_cursors(ConnectionClass *self);
extern GLOBAL_VALUES globals;
CSTR bgncmd = "BEGIN";
+CSTR cmtcmd = "COMMIT";
CSTR rbkcmd = "ROLLBACK";
CSTR semi_colon = ";";
/*
char ret = TRUE;
if (CC_is_in_trans(self))
{
- QResultClass *res = CC_send_query(self, "COMMIT", NULL, 0, NULL);
- mylog("CC_commit: sending COMMIT!\n");
- ret = QR_command_maybe_successful(res);
- QR_Destructor(res);
+ CC_close_eof_cursors(self);
+ if (CC_is_in_trans(self))
+ {
+ QResultClass *res = CC_send_query(self, cmtcmd, NULL, 0, NULL);
+ mylog("CC_commit: sending COMMIT!\n");
+ ret = QR_command_maybe_successful(res);
+ QR_Destructor(res);
+ }
}
return ret;
}
if (libpqopt)
{
- if (ci->sslmode[0])
+ switch (ci->sslmode[0])
{
- opts[cnt][0] = "sslmode"; opts[cnt++][1] = ci->sslmode;
+ case '\0':
+ break;
+ case 'd':
+ opts[cnt][0] = "sslmode";
+ opts[cnt++][1] = ci->sslmode;
+ break;
+ default:
+ opts[cnt][0] = "sslmode";
+ opts[cnt++][1] = ci->sslmode;
+ if (sslverify_needed())
+ {
+ opts[cnt][0] = "sslverify";
+ opts[cnt++][1] = "none";
+ }
}
if (ci->password[0])
{
}
+static int CC_close_eof_cursors(ConnectionClass *self)
+{
+ int i, ccount = 0;
+ StatementClass *stmt;
+ QResultClass *res;
+
+ if (!self->ncursors)
+ return ccount;
+ CONNLOCK_ACQUIRE(self);
+ for (i = 0; i < self->num_stmts; i++)
+ {
+ if (stmt = self->stmts[i], NULL == stmt)
+ continue;
+ if (res = SC_get_Result(stmt), NULL == res)
+ continue;
+ if (NULL != QR_get_cursor(res) &&
+ QR_is_withhold(res) &&
+ QR_once_reached_eof(res))
+ {
+ if (QR_get_num_cached_tuples(res) >= QR_get_num_total_tuples(res) ||
+ SQL_CURSOR_FORWARD_ONLY == stmt->options.cursor_type)
+ {
+ QR_close(res);
+ ccount++;
+ }
+ }
+ }
+ CONNLOCK_RELEASE(self);
+ return ccount;
+}
+
static void CC_clear_cursors(ConnectionClass *self, BOOL on_abort)
{
int i;
}
else
{
- if (strnicmp(cmdbuffer, "COMMIT", 6) == 0)
+ if (strnicmp(cmdbuffer, cmtcmd, 6) == 0)
CC_on_commit(self);
else if (strnicmp(cmdbuffer, "END", 3) == 0)
CC_on_commit(self);
#define __CONNECTION_H__
#include "psqlodbc.h"
+#include <time.h>
#include <stdlib.h>
#include <string.h>
struct col_info
{
Int2 num_reserved_cols;
+ Int2 refcnt;
QResultClass *result;
pgNAME schema_name;
pgNAME table_name;
OID table_oid;
+ time_t acc_time;
};
#define free_col_info_contents(coli) \
{ \
if (NULL != coli->result) \
QR_Destructor(coli->result); \
+ coli->result = NULL; \
NULL_THE_NAME(coli->schema_name); \
NULL_THE_NAME(coli->table_name); \
+ coli->table_oid = 0; \
+ coli->refcnt = 0; \
+ coli->acc_time = 0; \
}
#define col_info_initialize(coli) (memset(coli, 0, sizeof(COL_INFO)))
SC_set_planname(stmt, plan_name);
if (!(res = SendSyncAndReceive(stmt, NULL, "prepare_and_describe")))
{
- SC_set_error(stmt, STMT_EXEC_ERROR, "commnication error while preapreand_describe", func);
+ SC_set_error(stmt, STMT_NO_RESPONSE, "commnication error while preapreand_describe", func);
CC_on_abort(conn, CONN_DEAD);
goto cleanup;
}
goto cleanup;
if (!(res = SendSyncAndReceive(stmt, NULL, "prepare_and_describe")))
{
- SC_set_error(stmt, STMT_EXEC_ERROR, "commnication error while preapreand_describe", func);
+ SC_set_error(stmt, STMT_NO_RESPONSE, "commnication error while preapreand_describe", func);
CC_on_abort(conn, CONN_DEAD);
goto cleanup;
}
{
if (ti[i])
{
+ COL_INFO *coli = ti[i]->col_info;
+ if (coli)
+ {
+mylog("!!!refcnt %p:%d -> %d\n", coli, coli->refcnt, coli->refcnt - 1);
+ coli->refcnt--;
+ }
NULL_THE_NAME(ti[i]->schema_name);
NULL_THE_NAME(ti[i]->table_name);
NULL_THE_NAME(ti[i]->table_alias);
#define INI_SSLMODE "SSLmode"
#define ABBR_SSLMODE "CA"
#define INI_EXTRAOPTIONS "AB"
+#define INI_LOGDIR "Logdir"
#define SSLMODE_DISABLE "disable"
#define SSLMODE_ALLOW "allow"
case CONNECTION_COULD_NOT_SEND:
case CONNECTION_COULD_NOT_RECEIVE:
case CONNECTION_COMMUNICATION_ERROR:
+ case CONNECTION_NO_RESPONSE:
pg_sqlstate_set(env, szSqlState, "08S01", "08S01");
break;
default:
#ifdef WIN32
WORD wVersionRequested;
WSADATA wsaData;
- const int major = 1, minor = 1;
+ const int major = 2, minor = 2;
/* Load the WinSock Library */
wVersionRequested = MAKEWORD(major, minor);
return rv;
}
/* Verify that this is the minimum version of WinSock */
- if (LOBYTE(wsaData.wVersion) != major ||
- HIBYTE(wsaData.wVersion) != minor)
+ if (LOBYTE(wsaData.wVersion) >= 1 &&
+ (LOBYTE(wsaData.wVersion) >= 2 ||
+ HIBYTE(wsaData.wVersion) >= 1))
+ ;
+ else
{
mylog("%s: WSAStartup version=(%d,%d)\n", __FUNCTION__,
LOBYTE(wsaData.wVersion), HIBYTE(wsaData.wVersion));
{
CSTR func = "PGAPI_Transact";
ConnectionClass *conn;
- QResultClass *res;
- char ok,
- *stmt_string;
+ char ok;
int lf;
mylog("entering %s: hdbc=%p, henv=%p\n", func, hdbc, henv);
conn = (ConnectionClass *) hdbc;
- if (fType == SQL_COMMIT)
- stmt_string = "COMMIT";
- else if (fType == SQL_ROLLBACK)
- stmt_string = "ROLLBACK";
- else
+ if (fType != SQL_COMMIT &&
+ fType != SQL_ROLLBACK)
{
CC_set_error(conn, CONN_INVALID_ARGUMENT_NO, "PGAPI_Transact can only be called with SQL_COMMIT or SQL_ROLLBACK as parameter", func);
return SQL_ERROR;
/* If manual commit and in transaction, then proceed. */
if (!CC_is_in_autocommit(conn) && CC_is_in_trans(conn))
{
- mylog("PGAPI_Transact: sending on conn %d '%s'\n", conn, stmt_string);
+ mylog("PGAPI_Transact: sending on conn %p '%d'\n", conn, fType);
- res = CC_send_query(conn, stmt_string, NULL, 0, NULL);
- ok = QR_command_maybe_successful(res);
- QR_Destructor(res);
+ ok = (SQL_COMMIT == fType) ? CC_commit(conn) : CC_abort(conn);
if (!ok)
{
/* error msg will be in the connection */
#pragma comment(linker, "/Delay:UNLOAD")
#endif /* _MSC_VER */
#endif /* _MSC_VER */
+
#if defined(DYNAMIC_LOAD)
#define WIN_DYN_LOAD
-CSTR libpq = "libpq";
CSTR libpqdll = "LIBPQ.dll";
#ifdef UNICODE_SUPPORT
CSTR pgenlist = "pgenlist";
#endif /* DYNAMIC_LOAD */
#endif /* WIN32 */
+#ifndef NOT_USE_LIBPQ
+CSTR libpq = "libpq";
+CSTR checkproc = "PQconninfoParse";
+static int sslverify_available = -1;
+#endif /* NOT_USE_LIBPQ */
+
#if defined(_MSC_DELAY_LOAD_IMPORT)
static BOOL loaded_libpq = FALSE, loaded_ssllib = FALSE;
static BOOL loaded_pgenlist = FALSE;
{
if (hmodule = MODULE_load_from_psqlodbc_path(libpq), NULL == hmodule)
hmodule = LoadLibrary(libpq);
+#ifndef NOT_USE_LIBPQ
+ if (sslverify_available < 0 && NULL != hmodule)
+ {
+ if (NULL == GetProcAddress(hmodule, checkproc))
+ sslverify_available = FALSE;
+ else
+ sslverify_available = TRUE;
+ }
+#endif /* NOT_USE_LIBPQ */
}
else if (_strnicmp(pdli->szDll, pgenlist, strlen(pgenlist)) == 0)
{
#endif /* _MSC_DELAY_LOAD_IMPORT */
#ifndef NOT_USE_LIBPQ
+#if defined(_MSC_DELAY_LOAD_IMPORT)
+static int filter_conninfoParse(int level)
+{
+ switch (level & 0xffff)
+ {
+ case ERROR_MOD_NOT_FOUND:
+ case ERROR_PROC_NOT_FOUND:
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+#endif /* _MSC_DELAY_LOAD_IMPORT */
+
+BOOL sslverify_needed(void)
+{
+ if (sslverify_available < 0)
+ {
+#if defined(_MSC_DELAY_LOAD_IMPORT)
+ __try {
+ PQconninfoOption *conninfo;
+#if (_MSC_VER < 1300)
+ __pfnDliFailureHook = DliErrorHook;
+ __pfnDliNotifyHook = DliErrorHook;
+#else
+ __pfnDliFailureHook2 = DliErrorHook;
+ __pfnDliNotifyHook2 = DliErrorHook;
+#endif /* _MSC_VER */
+ if (conninfo = PQconninfoParse("sslverify=none", NULL), NULL == conninfo)
+ sslverify_available = 0;
+ {
+ sslverify_available = 1;
+ PQconninfoFree(conninfo);
+ }
+ }
+ __except (filter_conninfoParse(GetExceptionCode())) {
+ sslverify_available = 0;
+ }
+#else
+#ifdef HAVE_LIBLTDL
+ lt_dlhandle dlhandle = lt_dlopenext(libpq);
+
+ sslverify_available = 1;
+ if (NULL != dlhandle)
+ {
+ if (NULL == lt_dlsym(dlhandle, checkproc))
+ sslverify_available = 0;
+ lt_dlclose(dlhandle);
+ }
+#endif /* HAVE_LIBLTDL */
+#endif /* _MSC_DELAY_LOAD_IMPORT */
+ }
+
+ return (0 != sslverify_available);
+}
+
void *CALL_PQconnectdb(const char *conninfo, BOOL *libpqLoaded)
{
void *pqconn = NULL;
#define __LOADLIB_H__
#include "psqlodbc.h"
+#ifdef HAVE_LIBLTDL
+#include <ltdl.h>
+#else
+#ifdef HAVE_DLFCN_H
+#include <dlfcn.h>
+#endif /* HAVE_DLFCN_H */
+#endif /* HAVE_LIBLTDL */
#include <stdlib.h>
#ifdef __cplusplus
BOOL SSLLIB_check(void);
#ifndef NOT_USE_LIBPQ
void *CALL_PQconnectdb(const char *conninfo, BOOL *);
+BOOL sslverify_needed(void);
#endif /* NOT_USE_LIBPQ */
#ifdef _HANDLE_ENLIST_IN_DTC_
RETCODE CALL_EnlistInDtc(ConnectionClass *conn, void * pTra, int method);
*/
#include "psqlodbc.h"
+#include "dlg_specific.h"
#include <stdio.h>
#include <stdlib.h>
#endif
extern GLOBAL_VALUES globals;
-void generate_filename(const char *, const char *, char *);
+
+static char *logdir = NULL;
void
generate_filename(const char *dirname, const char *prefix, char *filename)
if (!MLOGFP)
{
- generate_filename(MYLOGDIR, MYLOGFILE, filebuf);
+ generate_filename(logdir ? logdir : MYLOGDIR, MYLOGFILE, filebuf);
MLOGFP = fopen(filebuf, PG_BINARY_A);
if (!MLOGFP)
{
MLOGFP = fopen(filebuf, PG_BINARY_A);
if (!MLOGFP)
{
- generate_filename("c:\\podbclog", MYLOGFILE, filebuf);
+ generate_filename("C:\\podbclog", MYLOGFILE, filebuf);
MLOGFP = fopen(filebuf, PG_BINARY_A);
}
}
if (!MLOGFP)
{
- generate_filename(MYLOGDIR, MYLOGFILE, filebuf);
+ generate_filename(logdir ? logdir : MYLOGDIR, MYLOGFILE, filebuf);
MLOGFP = fopen(filebuf, PG_BINARY_A);
if (MLOGFP)
setbuf(MLOGFP, NULL);
if (!QLOGFP)
{
- generate_filename(QLOGDIR, QLOGFILE, filebuf);
+ generate_filename(logdir ? logdir : QLOGDIR, QLOGFILE, filebuf);
QLOGFP = fopen(filebuf, PG_BINARY_A);
if (!QLOGFP)
{
void InitializeLogging()
{
+ char dir[PATH_MAX];
+
+ SQLGetPrivateProfileString(DBMS_NAME, INI_LOGDIR, "", dir, sizeof(dir), ODBCINST_INI);
+ if (dir[0])
+ logdir = strdup(dir);
mylog_initialize();
qlog_initialize();
}
{
mylog_finalize();
qlog_finalize();
+ if (logdir)
+ {
+ free(logdir);
+ logdir = NULL;
+ }
}
#define FLD_INCR 32
#define TAB_INCR 8
#define COLI_INCR 16
+#define COLI_RECYCLE 128
static char *getNextToken(int ccsc, char escape_in_literal, char *s, char *token, int smax, char *delim, char *quote, char *dquote, char *numeric);
static void getColInfo(COL_INFO *col_info, FIELD_INFO *fi, int k);
* Though current_schema() doesn't have
* much sense in PostgreSQL, we first
* check the current_schema() when no
- * explicit schema name was specified.
+ * explicit schema name is specified.
*/
for (colidx = 0; colidx < conn->ntables; colidx++)
{
&& res != NULL && QR_get_num_cached_tuples(res) > 0)
{
BOOL coli_exist = FALSE;
- COL_INFO *coli = NULL;
+ COL_INFO *coli = NULL, *ccoli = NULL, *tcoli;
+ int k;
+ time_t acctime = 0;
mylog(" Success\n");
if (greloid != 0)
{
- int k;
-
for (k = 0; k < conn->ntables; k++)
{
- if (conn->col_info[k]->table_oid == greloid)
+ tcoli = conn->col_info[k];
+ if (tcoli->table_oid == greloid)
+ {
+ coli = tcoli;
+ coli_exist = TRUE;
+ break;
+ }
+ }
+ }
+ if (!coli_exist)
+ {
+ for (k = 0; k < conn->ntables; k++)
+ {
+ tcoli = conn->col_info[k];
+ if (0 < tcoli->refcnt)
+ continue;
+ if ((0 == tcoli->table_oid &&
+ NAME_IS_NULL(tcoli->table_name)) ||
+ strnicmp(SAFE_NAME(tcoli->schema_name), "pg_temp_", 8) == 0)
{
- coli = conn->col_info[k];
+ coli = tcoli;
coli_exist = TRUE;
break;
}
+ if (NULL == ccoli ||
+ tcoli->acc_time < acctime)
+ {
+ ccoli = tcoli;
+ acctime = tcoli->acc_time;
+ }
+ }
+ if (!coli_exist &&
+ NULL != ccoli &&
+ conn->ntables >= COLI_RECYCLE)
+ {
+ coli_exist = TRUE;
+ coli = ccoli;
}
}
if (coli_exist)
if (conn->ntables >= conn->coli_allocated)
{
Int2 new_alloc;
+ COL_INFO **col_info;
new_alloc = conn->coli_allocated * 2;
if (new_alloc <= conn->ntables)
new_alloc = COLI_INCR;
mylog("PARSE: Allocating col_info at ntables=%d\n", conn->ntables);
- conn->col_info = (COL_INFO **) realloc(conn->col_info, new_alloc * sizeof(COL_INFO *));
- if (!conn->col_info)
+ col_info = (COL_INFO **) realloc(conn->col_info, new_alloc * sizeof(COL_INFO *));
+ if (!col_info)
{
if (stmt)
SC_set_error(stmt, STMT_NO_MEMORY_ERROR, "PGAPI_AllocStmt failed in parse_statement for col_info.", __FUNCTION__);
goto cleanup;
}
+ conn->col_info = col_info;
conn->coli_allocated = new_alloc;
}
mylog("Created col_info table='%s', ntables=%d\n", PRINT_NAME(wti->table_name), conn->ntables);
/* Associate a table from the statement with a SQLColumn info */
found = TRUE;
+ coli->refcnt++;
wti->col_info = coli;
}
cleanup:
if (!wti) /* SQLColAttribute case */
{
int i;
+
if (0 == greloid)
return FALSE;
+ if (!stmt)
+ return FALSE;
colatt = TRUE;
for (i = 0; i < stmt->ntab; i++)
{
else if (NULL != coli)
{
found = TRUE;
+ coli->refcnt++;
wti->col_info = coli;
}
}
if (stmt)
ColAttSet(stmt, wti);
}
+ wti->col_info->acc_time = SC_get_time(stmt);
}
else if (!colatt && stmt)
SC_set_parse_status(stmt, STMT_PARSE_FATAL);
*
* Comments: See "notice.txt" for copyright and license information.
*
- * $Id: psqlodbc.h,v 1.124 2008/11/23 01:00:53 hinoue Exp $
+ * $Id: psqlodbc.h,v 1.125 2009/03/20 15:39:22 hinoue Exp $
*
*/
#define FALSE (BOOL)0
#endif /* FALSE */
#else
+#if (_MSC_VER >= 1400)
+#define snprintf sprintf_s
+#define strncat(d, s, l) strcat_s(d, l, s)
+#else
#define snprintf _snprintf
+#endif
#ifndef strdup
#define strdup _strdup
#endif /* strdup */
#define WCLEN sizeof(SQLWCHAR)
SQLULEN ucs2strlen(const SQLWCHAR *ucs2str);
char *ucs2_to_utf8(const SQLWCHAR *ucs2str, SQLLEN ilen, SQLLEN *olen, BOOL tolower);
-SQLULEN utf8_to_ucs2_lf(const char * utf8str, SQLLEN ilen, BOOL lfconv, SQLWCHAR *ucs2str, SQLULEN buflen);
+SQLLEN utf8_to_ucs2_lf(const char * utf8str, SQLLEN ilen, BOOL lfconv, SQLWCHAR *ucs2str, SQLULEN buflen);
#define utf8_to_ucs2(utf8str, ilen, ucs2str, buflen) utf8_to_ucs2_lf(utf8str, ilen, FALSE, ucs2str, buflen)
#endif /* UNICODE_SUPPORT */
if (!CC_is_in_autocommit(conn) && CC_is_in_trans(conn))
{
if (SC_is_pre_executable(self) && !SC_is_parse_tricky(self))
- CC_abort(conn);
+ /* CC_abort(conn) */;
}
break;
{ STMT_OPTION_NOT_FOR_THE_DRIVER, "HYC00", "HYC00" },
{ STMT_FETCH_OUT_OF_RANGE, "HY106", "S1106" },
{ STMT_COUNT_FIELD_INCORRECT, "07002", "07002" },
- { STMT_INVALID_NULL_ARG, "HY009", "S1009" }
+ { STMT_INVALID_NULL_ARG, "HY009", "S1009" },
+ { STMT_NO_RESPONSE, "08S01", "08S01" }
};
static PG_ErrorInfo *
if (!(res = SendSyncAndReceive(self, self->curr_param_result ? res : NULL, "bind_and_execute")))
{
if (SC_get_errornumber(self) <= 0)
- SC_set_error(self, STMT_EXEC_ERROR, "Could not receive the response, communication down ??", func);
+ SC_set_error(self, STMT_NO_RESPONSE, "Could not receive the response, communication down ??", func);
CC_on_abort(conn, CONN_DEAD);
goto cleanup;
}
id = SOCK_get_id(sock);
if ((SOCK_get_errcode(sock) != 0) || (id == EOF))
{
- SC_set_error(stmt, CONNECTION_NO_RESPONSE, "No response rom the backend", func);
+ SC_set_error(stmt, STMT_NO_RESPONSE, "No response rom the backend", func);
mylog("%s: 'id' - %s\n", func, SC_get_errormsg(stmt));
CC_on_abort(conn, CONN_DEAD);
,STMT_FETCH_OUT_OF_RANGE
,STMT_COUNT_FIELD_INCORRECT
,STMT_INVALID_NULL_ARG
+ ,STMT_NO_RESPONSE
};
/* statement types */
#ifndef __VERSION_H__
#define __VERSION_H__
-#define POSTGRESDRIVERVERSION "08.03.0401"
-#define POSTGRES_RESOURCE_VERSION "08.03.0401\0"
-#define PG_DRVFILE_VERSION 8,3,04,01
-#define PG_BUILD_VERSION "200901180001"
+#define POSTGRESDRIVERVERSION "08.03.0402"
+#define POSTGRES_RESOURCE_VERSION "08.03.0402\0"
+#define PG_DRVFILE_VERSION 8,3,04,02
+#define PG_BUILD_VERSION "200903200002"
#endif