From 35b56ef9573e817549281ff20d031bae57ec81e8 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Wed, 13 Jul 2005 14:23:36 +0000 Subject: [PATCH] New version of the driver with libpq based comms layer, courtesy of Anoop Kumar of Pervasive PostgreSQL. Version number bumped, and extra strings modified in the resource file to make it obvious when the libpq version of the driver is being run. --- Makefile.am | 4 +- README.unix | 3 + bind.h | 4 +- columninfo.c | 17 + columninfo.h | 4 +- configure.ac | 98 +- connection.c | 4156 +++++++++++++++++++++++++++----------------- connection.h | 4 + convert.c | 62 +- descriptor.c | 6 + dlg_specific.c | 6 + dlg_specific.h | 5 + drvconn.c | 4 + environ.c | 23 +- execute.c | 48 +- info.c | 8 +- info30.c | 21 +- installer/Make.bat | 2 +- libpqconnection.h | 420 +++++ lobj.c | 4 + lobj.h | 4 + misc.c | 8 +- multibyte.c | 22 +- odbcapi.c | 30 +- odbcapi30.c | 14 +- odbcapi30w.c | 14 +- odbcapiw.c | 26 +- options.c | 6 + parse.c | 8 +- pgapi30.c | 148 +- pgtypes.c | 6 + psqlodbc.c | 8 +- psqlodbc.h | 6 +- psqlodbc.rc | 34 +- qresult.c | 320 +++- qresult.h | 31 +- readme.txt | 21 +- results.c | 11 +- setup.c | 5 + socket.c | 13 +- statement.c | 34 +- tuplelist.c | 6 +- version.h | 8 +- win32.mak | 85 +- 44 files changed, 3961 insertions(+), 1806 deletions(-) create mode 100644 libpqconnection.h diff --git a/Makefile.am b/Makefile.am index dff8ccb..65069d1 100644 --- a/Makefile.am +++ b/Makefile.am @@ -2,7 +2,7 @@ # # Makefile.am for psqlodbc (PostgreSQL ODBC driver) # -# $Header: /home/heikki/psqlodbc-cvs-copy/psqlodbc/Makefile.am,v 1.17 2005/03/02 14:37:52 dpage Exp $ +# $Header: /home/heikki/psqlodbc-cvs-copy/psqlodbc/Makefile.am,v 1.18 2005/07/13 14:23:34 dpage Exp $ # #------------------------------------------------------------------------- @@ -23,7 +23,7 @@ psqlodbc_la_SOURCES = \ psqlodbc_la_SOURCES += \ bind.h columninfo.h connection.h convert.h descriptor.h \ dlg_specific.h environ.h \ - lobj.h md5.h misc.h multibyte.h pgapifunc.h pgtypes.h \ + lobj.h libpqconnection.h md5.h misc.h multibyte.h pgapifunc.h pgtypes.h \ psqlodbc.h qresult.h resource.h socket.h statement.h tuple.h \ tuplelist.h version.h diff --git a/README.unix b/README.unix index 8943e49..ce974eb 100644 --- a/README.unix +++ b/README.unix @@ -21,6 +21,9 @@ The configure script will accept the following useful options: --with-iodbc --with-unixodbc --with-odbcinst=DIR +--with-libpq (build a libpq enabled version of psqlodbc. default = yes) +--with-pgsql-include=DIR (Look for postgreSQL include files in DIR if libpq is enabled) +--with-pgsql=DIR (look for PostgreSQL libraries and headers in DIR if libpq is enabled) --enable-pthreads (thread-safe driver on some platforms) --help diff --git a/bind.h b/bind.h index cd9fb0c..16d08fa 100644 --- a/bind.h +++ b/bind.h @@ -10,7 +10,9 @@ #define __BIND_H__ #include "psqlodbc.h" - +#ifdef USE_LIBPQ +#include +#endif /* USE_LIBPQ */ /* * BindInfoClass -- stores information about a bound column */ diff --git a/columninfo.c b/columninfo.c index 6897711..42438cd 100644 --- a/columninfo.c +++ b/columninfo.c @@ -15,8 +15,13 @@ #include "pgtypes.h" #include "columninfo.h" +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" #include "socket.h" +#endif /* USE_LIBPQ*/ + #include #include #include "pgapifunc.h" @@ -51,6 +56,17 @@ CI_Destructor(ColumnInfoClass *self) } +#ifdef USE_LIBPQ + +/* Reading Fields part is already done in the mapping part, skipping it while using Libpq */ +char +CI_read_fields(ColumnInfoClass *self, ConnectionClass *conn) +{ + return 'T'; +} + +#else + /* * Read in field descriptions. * If self is not null, then also store the information. @@ -120,6 +136,7 @@ CI_read_fields(ColumnInfoClass *self, ConnectionClass *conn) return (SOCK_get_errcode(sock) == 0); } +#endif /* USE_LIBPQ */ void CI_free_memory(ColumnInfoClass *self) diff --git a/columninfo.h b/columninfo.h index 41e9400..5f9951f 100644 --- a/columninfo.h +++ b/columninfo.h @@ -10,7 +10,9 @@ #define __COLUMNINFO_H__ #include "psqlodbc.h" - +#ifdef USE_LIBPQ +#include +#endif /* USE_LIBPQ */ struct ColumnInfoClass_ { Int2 num_fields; diff --git a/configure.ac b/configure.ac index 247d070..6b0ab8f 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ # Process this file with autoconf to produce a configure script. -AC_INIT(psqlodbc, 08.00.0102, [pgsql-odbc@postgresql.org]) +AC_INIT(psqlodbc, 08.01.0001, [pgsql-odbc@postgresql.org]) AC_PREREQ(2.52) AM_INIT_AUTOMAKE AC_CONFIG_SRCDIR([bind.c]) @@ -44,7 +44,6 @@ if test "$with_iodbc" = yes; then AC_DEFINE(WITH_IODBC, 1, [Define to 1 to build with iODBC support]) fi - # # Default odbc version number (--with-odbcver), default 0x0300 # @@ -56,7 +55,6 @@ AC_MSG_RESULT([$with_odbcver]) AC_DEFINE_UNQUOTED(ODBCVER, [$with_odbcver], [Define to ODBC version (--with-odbcver)]) AC_CHECK_FUNCS(strtoul strtoll) -AC_CHECK_LIB(socket, socket) # to implement the thread-safe driver PGAC_ARG_BOOL(enable, pthreads, no, @@ -107,5 +105,99 @@ AC_C_CONST AC_TYPE_SIZE_T AC_STRUCT_TM +#Decide whether libpq or socket based driver to be built +PGAC_ARG_BOOL(with, libpq, yes, + [ --with-libpq build libpq enabled odbc driver(default)],[use_libpq=yes],[use_libpq=no]) + +# +#Options for pgsql headers and libraries +# +if test "$use_libpq" = "yes"; then + AC_MSG_CHECKING(for pgsql include files) + AC_ARG_WITH(pgsql-include, + [ --with-pgsql-include=DIR Look for postgreSQL include files in DIR], + [if test "$withval" != no; then + AC_MSG_RESULT(yes) + pgsql_include="$withval" + else + AC_MSG_RESULT(no) + AC_MSG_ERROR([you must specify a directory when using --with-pgsql-include=DIR]) + fi]) +fi + +# +# Library directories +# +if test "$use_libpq" = "yes"; then + AC_MSG_CHECKING(for pgsql) + AC_ARG_WITH(pgsql, + [ --with-pgsql=DIR look for PostgreSQL libraries and headers in DIR], + [if test "$withval" != no + then + AC_MSG_RESULT(yes) + LIBPQ_HOME="$withval" + else + AC_MSG_RESULT(no) + fi], [ + AC_MSG_RESULT(yes) + LIBPQ_HOME="/usr/local/pgsql" + if test "$pgsql_include" = "" + then + if test ! -f "${LIBPQ_HOME}/include/libpq-fe.h" + then + LIBPQ_HOME=/usr/local + if test ! -f "${LIBPQ_HOME}/include/libpq-fe.h" + then + LIBPQ_HOME=/usr + fi + fi + if test -f "/usr/include/libpq-fe.h" + then + pgsql_include="/usr/include" + else + if test -f "/usr/include/pgsql/libpq-fe.h" + then + pgsql_include="/usr/include/pgsql" + fi + fi + fi + ]) +# +# Check for libpq libraries and headers +# + if test -n "${LIBPQ_HOME}" + then + OLD_LDFLAGS="$LDFLAGS" + OLD_CPPFLAGS="$CPPFLAGS" + LDFLAGS="$LDFLAGS -L${LIBPQ_HOME}/lib" + AC_CHECK_LIB(pq, PQexec, [pgsql_cv_libpq=yes], [pgsql_cv_libpq=no]) + + if test "$pgsql_include" != "" + then + CPPFLAGS="$CPPFLAGS -I${pgsql_include}" + else + CPPFLAGS="$CPPFLAGS -I${LIBPQ_HOME}/include" + fi + AC_CHECK_HEADER(libpq-fe.h, [pgsql_cv_libpqfe_h=yes], [pgsql_cv_libpqfe_h=no]) + + if test "$pgsql_cv_libpq" = "yes" -a "$pgsql_cv_libpqfe_h" = "yes" + then + AC_MSG_CHECKING(pgsql in ${LIBPQ_HOME}) + AC_MSG_RESULT(ok) + else + AC_MSG_CHECKING(pgsql in ${LIBPQ_HOME}) + AC_MSG_RESULT(failed) + LDFLAGS="$OLD_LDFLAGS" + CPPFLAGS="$OLD_CPPFLAGS" + AC_MSG_ERROR([you must specify a valid pgsql installation with --with-pgsql=DIR]) + fi + LIBS="$LIBS -lpq" + CPPFLAGS="$CPPFLAGS -DUSE_LIBPQ" + + fi +else + AC_CHECK_LIB(socket, socket) +fi + AC_CONFIG_FILES([Makefile]) AC_OUTPUT diff --git a/connection.c b/connection.c index deda580..01e4ca8 100644 --- a/connection.c +++ b/connection.c @@ -14,8 +14,6 @@ */ /* Multibyte support Eiji Tokuya 2001-03-15 */ -#include "connection.h" - #include #include #include @@ -24,7 +22,6 @@ #endif /* WIN32 */ #include "environ.h" -#include "socket.h" #include "statement.h" #include "qresult.h" #include "lobj.h" @@ -42,6 +39,15 @@ extern GLOBAL_VALUES globals; +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#include "pgtypes.h" +#include +#else +#include "connection.h" +#include "socket.h" +#endif /* USE_LIBPQ */ + RETCODE SQL_API PGAPI_AllocConnect( @@ -245,118 +251,6 @@ CC_conninfo_init(ConnInfo *conninfo) conninfo->use_server_side_prepare = -1; memcpy(&(conninfo->drivers), &globals, sizeof(globals)); } -/* - * IMPLEMENTATION CONNECTION CLASS - */ -ConnectionClass * -CC_Constructor() -{ - ConnectionClass *rv; - - rv = (ConnectionClass *) malloc(sizeof(ConnectionClass)); - - if (rv != NULL) - { - rv->henv = NULL; /* not yet associated with an environment */ - - rv->__error_message = NULL; - rv->__error_number = 0; - rv->errormsg_created = FALSE; - - rv->status = CONN_NOT_CONNECTED; - rv->transact_status = CONN_IN_AUTOCOMMIT; /* autocommit by default */ - - CC_conninfo_init(&(rv->connInfo)); - rv->sock = SOCK_Constructor(rv); - if (!rv->sock) - return NULL; - - rv->stmts = (StatementClass **) malloc(sizeof(StatementClass *) * STMT_INCREMENT); - if (!rv->stmts) - return NULL; - memset(rv->stmts, 0, sizeof(StatementClass *) * STMT_INCREMENT); - - rv->num_stmts = STMT_INCREMENT; - rv->descs = (DescriptorClass **) malloc(sizeof(DescriptorClass *) * STMT_INCREMENT); - if (!rv->descs) - return NULL; - memset(rv->descs, 0, sizeof(DescriptorClass *) * STMT_INCREMENT); - - rv->num_descs = STMT_INCREMENT; - - rv->lobj_type = PG_TYPE_LO_UNDEFINED; - - rv->ntables = 0; - rv->col_info = NULL; - - rv->translation_option = 0; - rv->translation_handle = NULL; - rv->DataSourceToDriver = NULL; - rv->DriverToDataSource = NULL; - rv->driver_version = ODBCVER; - memset(rv->pg_version, 0, sizeof(rv->pg_version)); - rv->pg_version_number = .0; - rv->pg_version_major = 0; - rv->pg_version_minor = 0; - rv->ms_jet = 0; - rv->unicode = 0; - rv->result_uncommitted = 0; - rv->schema_support = 0; - rv->isolation = SQL_TXN_READ_COMMITTED; - rv->client_encoding = NULL; - rv->server_encoding = NULL; - rv->current_schema = NULL; - rv->num_discardp = 0; - rv->discardp = NULL; - - /* Initialize statement options to defaults */ - /* Statements under this conn will inherit these options */ - - InitializeStatementOptions(&rv->stmtOptions); - InitializeARDFields(&rv->ardOptions); - InitializeAPDFields(&rv->apdOptions); - INIT_CONN_CS(rv); - } - return rv; -} - - -char -CC_Destructor(ConnectionClass *self) -{ - mylog("enter CC_Destructor, self=%u\n", self); - - if (self->status == CONN_EXECUTING) - return 0; - - CC_cleanup(self); /* cleanup socket and statements */ - - mylog("after CC_Cleanup\n"); - - /* Free up statement holders */ - if (self->stmts) - { - free(self->stmts); - self->stmts = NULL; - } - - if (self->descs) - { - free(self->descs); - self->descs = NULL; - } - - mylog("after free statement holders\n"); - - if (self->__error_message) - free(self->__error_message); - DELETE_CONN_CS(self); - free(self); - - mylog("exit CC_Destructor\n"); - - return 1; -} /* Return how many cursors are opened on this connection */ @@ -462,121 +356,6 @@ CC_abort(ConnectionClass *self) } -/* This is called by SQLDisconnect also */ -char -CC_cleanup(ConnectionClass *self) -{ - int i; - StatementClass *stmt; - DescriptorClass *desc; - - if (self->status == CONN_EXECUTING) - return FALSE; - - mylog("in CC_Cleanup, self=%u\n", self); - - /* Cancel an ongoing transaction */ - /* We are always in the middle of a transaction, */ - /* even if we are in auto commit. */ - if (self->sock) - { - CC_abort(self); - - mylog("after CC_abort\n"); - - /* This actually closes the connection to the dbase */ - SOCK_Destructor(self->sock); - self->sock = NULL; - } - - mylog("after SOCK destructor\n"); - - /* Free all the stmts on this connection */ - for (i = 0; i < self->num_stmts; i++) - { - stmt = self->stmts[i]; - if (stmt) - { - stmt->hdbc = NULL; /* prevent any more dbase interactions */ - - SC_Destructor(stmt); - - self->stmts[i] = NULL; - } - } - - /* Free all the descs on this connection */ - for (i = 0; i < self->num_descs; i++) - { - desc = self->descs[i]; - if (desc) - { - DC_get_conn(desc) = NULL; /* prevent any more dbase interactions */ - DC_Destructor(desc); - free(desc); - self->descs[i] = NULL; - } - } - - /* Check for translation dll */ -#ifdef WIN32 - if (self->translation_handle) - { - FreeLibrary(self->translation_handle); - self->translation_handle = NULL; - } -#endif - - self->status = CONN_NOT_CONNECTED; - self->transact_status = CONN_IN_AUTOCOMMIT; - CC_conninfo_init(&(self->connInfo)); - if (self->client_encoding) - { - free(self->client_encoding); - self->client_encoding = NULL; - } - if (self->server_encoding) - { - free(self->server_encoding); - self->server_encoding = NULL; - } - if (self->current_schema) - { - free(self->current_schema); - self->current_schema = NULL; - } - /* Free cached table info */ - if (self->col_info) - { - for (i = 0; i < self->ntables; i++) - { - if (self->col_info[i]->result) /* Free the SQLColumns result structure */ - QR_Destructor(self->col_info[i]->result); - - if (self->col_info[i]->schema) - free(self->col_info[i]->schema); - free(self->col_info[i]); - } - free(self->col_info); - self->col_info = NULL; - } - self->ntables = 0; - if (self->num_discardp > 0 && self->discardp) - { - for (i = 0; i < self->num_discardp; i++) - free(self->discardp[i]); - self->num_discardp = 0; - } - if (self->discardp) - { - free(self->discardp); - self->discardp = NULL; - } - - mylog("exit CC_Cleanup\n"); - return TRUE; -} - int CC_set_translation(ConnectionClass *self) @@ -619,1547 +398,2586 @@ CC_set_translation(ConnectionClass *self) return TRUE; } -static int -md5_auth_send(ConnectionClass *self, const char *salt) + +char +CC_add_statement(ConnectionClass *self, StatementClass *stmt) { - char *pwd1 = NULL, *pwd2 = NULL; - ConnInfo *ci = &(self->connInfo); - SocketClass *sock = self->sock; + int i; - if (!(pwd1 = malloc(MD5_PASSWD_LEN + 1))) - return 1; - if (!EncryptMD5(ci->password, ci->username, strlen(ci->username), pwd1)) - { - free(pwd1); - return 1; - } - if (!(pwd2 = malloc(MD5_PASSWD_LEN + 1))) + mylog("CC_add_statement: self=%u, stmt=%u\n", self, stmt); + + for (i = 0; i < self->num_stmts; i++) { - free(pwd1); - return 1; - } - if (!EncryptMD5(pwd1 + strlen("md5"), salt, 4, pwd2)) - { - free(pwd2); - free(pwd1); - return 1; + if (!self->stmts[i]) + { + stmt->hdbc = self; + self->stmts[i] = stmt; + return TRUE; + } } - free(pwd1); - SOCK_put_int(sock, 4 + strlen(pwd2) + 1, 4); - SOCK_put_n_char(sock, pwd2, strlen(pwd2) + 1); - SOCK_flush_output(sock); - free(pwd2); - return 0; -} -char -CC_connect(ConnectionClass *self, char password_req, char *salt_para) -{ - StartupPacket sp; - StartupPacket6_2 sp62; - QResultClass *res; - SocketClass *sock; - ConnInfo *ci = &(self->connInfo); - int areq = -1; - int beresp; - char msgbuffer[ERROR_MSG_LENGTH]; - char salt[5], notice[512]; - CSTR func = "CC_connect"; - char *encoding; + /* no more room -- allocate more memory */ + self->stmts = (StatementClass **) realloc(self->stmts, sizeof(StatementClass *) * (STMT_INCREMENT + self->num_stmts)); + if (!self->stmts) + return FALSE; - mylog("%s: entering...\n", func); + memset(&self->stmts[self->num_stmts], 0, sizeof(StatementClass *) * STMT_INCREMENT); - if (password_req != AUTH_REQ_OK) + stmt->hdbc = self; + self->stmts[self->num_stmts] = stmt; - sock = self->sock; /* already connected, just authenticate */ + self->num_stmts += STMT_INCREMENT; - else - { - qlog("Global Options: Version='%s', fetch=%d, socket=%d, unknown_sizes=%d, max_varchar_size=%d, max_longvarchar_size=%d\n", - POSTGRESDRIVERVERSION, - ci->drivers.fetch_max, - ci->drivers.socket_buffersize, - ci->drivers.unknown_sizes, - ci->drivers.max_varchar_size, - ci->drivers.max_longvarchar_size); - qlog(" disable_optimizer=%d, ksqo=%d, unique_index=%d, use_declarefetch=%d\n", - ci->drivers.disable_optimizer, - ci->drivers.ksqo, - ci->drivers.unique_index, - ci->drivers.use_declarefetch); - qlog(" text_as_longvarchar=%d, unknowns_as_longvarchar=%d, bools_as_char=%d NAMEDATALEN=%d\n", - ci->drivers.text_as_longvarchar, - ci->drivers.unknowns_as_longvarchar, - ci->drivers.bools_as_char, - TABLE_NAME_STORAGE_LEN); + return TRUE; +} - encoding = check_client_encoding(ci->conn_settings); - if (encoding && strcmp(encoding, "OTHER")) - self->client_encoding = strdup(encoding); - else - { - encoding = check_client_encoding(ci->drivers.conn_settings); - if (encoding && strcmp(encoding, "OTHER")) - self->client_encoding = strdup(encoding); - } - if (self->client_encoding) - self->ccsc = pg_CS_code(self->client_encoding); - qlog(" extra_systable_prefixes='%s', conn_settings='%s' conn_encoding='%s'\n", - ci->drivers.extra_systable_prefixes, - ci->drivers.conn_settings, - encoding ? encoding : ""); - if (self->status != CONN_NOT_CONNECTED) - { - CC_set_error(self, CONN_OPENDB_ERROR, "Already connected."); - return 0; - } +char +CC_remove_statement(ConnectionClass *self, StatementClass *stmt) +{ + int i; - if (ci->port[0] == '\0' || -#ifdef WIN32 - ci->server[0] == '\0' || -#endif /* WIN32 */ - ci->database[0] == '\0') + if (stmt->status == STMT_EXECUTING) + return FALSE; + for (i = 0; i < self->num_stmts; i++) + { + if (self->stmts[i] == stmt) { - CC_set_error(self, CONN_INIREAD_ERROR, "Missing server name, port, or database name in call to CC_connect."); - return 0; + self->stmts[i] = NULL; + return TRUE; } + } - mylog("CC_connect(): DSN = '%s', server = '%s', port = '%s'," -#ifdef HAVE_UNIX_SOCKETS - " uds = '%s'," -#endif - " database = '%s', username = '%s'," - " password='%s'\n", ci->dsn, ci->server, ci->port, -#ifdef HAVE_UNIX_SOCKETS - ci->uds, -#endif - ci->database, ci->username, ci->password ? "xxxxx" : ""); + return FALSE; +} -another_version_retry: - /* - * If the socket was closed for some reason (like a SQLDisconnect, - * but no SQLFreeConnect then create a socket now. - */ - if (!self->sock) - { - self->sock = SOCK_Constructor(self); - if (!self->sock) - { - CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Could not open a socket to the server"); - return 0; - } - } +void +CC_set_error(ConnectionClass *self, int number, const char *message) +{ + if (self->__error_message) + free(self->__error_message); + self->__error_number = number; + self->__error_message = message ? strdup(message) : NULL; +} - sock = self->sock; - mylog("connecting to the server socket...\n"); +void +CC_set_errormsg(ConnectionClass *self, const char *message) +{ + if (self->__error_message) + free(self->__error_message); + self->__error_message = message ? strdup(message) : NULL; +} - SOCK_connect_to(sock, (short) atoi(ci->port), ci->server -#ifdef HAVE_UNIX_SOCKETS - , ci->uds -#endif - ); - if (SOCK_get_errcode(sock) != 0) - { - mylog("connection to the server socket failed.\n"); - CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Could not connect to the server"); - return 0; - } - mylog("connection to the server socket succeeded.\n"); - if (PROTOCOL_62(ci)) - { - sock->reverse = TRUE; /* make put_int and get_int work - * for 6.2 */ +char +CC_get_error(ConnectionClass *self, int *number, char **message) +{ + int rv; + char *msgcrt; - memset(&sp62, 0, sizeof(StartupPacket6_2)); - SOCK_put_int(sock, htonl(4 + sizeof(StartupPacket6_2)), 4); - sp62.authtype = htonl(NO_AUTHENTICATION); - strncpy(sp62.database, ci->database, PATH_SIZE); - strncpy(sp62.user, ci->username, USRNAMEDATALEN); - SOCK_put_n_char(sock, (char *) &sp62, sizeof(StartupPacket6_2)); - SOCK_flush_output(sock); - } - else - { - memset(&sp, 0, sizeof(StartupPacket)); + mylog("enter CC_get_error\n"); - mylog("sizeof startup packet = %d\n", sizeof(StartupPacket)); + /* Create a very informative errormsg if it hasn't been done yet. */ + if (!self->errormsg_created) + { + msgcrt = CC_create_errormsg(self); + if (self->__error_message) + free(self->__error_message); + self->__error_message = msgcrt; + self->errormsg_created = TRUE; + } - /* Send length of Authentication Block */ - SOCK_put_int(sock, 4 + sizeof(StartupPacket), 4); + if (CC_get_errornumber(self)) + { + *number = CC_get_errornumber(self); + *message = CC_get_errormsg(self); + } + rv = (CC_get_errornumber(self) != 0); - if (PROTOCOL_63(ci)) - sp.protoVersion = (ProtocolVersion) htonl(PG_PROTOCOL_63); - else - sp.protoVersion = (ProtocolVersion) htonl(PG_PROTOCOL_LATEST); + self->__error_number = 0; /* clear the error */ - strncpy(sp.database, ci->database, SM_DATABASE); - strncpy(sp.user, ci->username, SM_USER); + mylog("exit CC_get_error\n"); - SOCK_put_n_char(sock, (char *) &sp, sizeof(StartupPacket)); - SOCK_flush_output(sock); - } + return rv; +} - mylog("sent the authentication block.\n"); +static void CC_clear_cursors(ConnectionClass *self, BOOL allcursors) +{ + int i; + StatementClass *stmt; + QResultClass *res; - if (sock->errornumber != 0) + for (i = 0; i < self->num_stmts; i++) + { + stmt = self->stmts[i]; + if (stmt && (res = SC_get_Result(stmt)) && + res->cursor && res->cursor[0]) { - mylog("couldn't send the authentication block properly.\n"); - CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Sending the authentication packet failed"); - return 0; + /* + * non-holdable cursors are automatically closed + * at commit time. + * all cursors are automatically closed + * at rollback time. + */ + if (res->cursor) + { + free(res->cursor); + res->cursor = NULL; + } } - mylog("sent the authentication block successfully.\n"); } +} - - mylog("gonna do authentication\n"); +void CC_on_commit(ConnectionClass *conn) +{ + if (CC_is_in_trans(conn)) + { +#ifdef DRIVER_CURSOR_IMPLEMENT + if (conn->result_uncommitted) + ProcessRollback(conn, FALSE); +#endif /* DRIVER_CURSOR_IMPLEMENT */ + CC_set_no_trans(conn); + CC_set_no_manual_trans(conn); + } + conn->result_uncommitted = 0; + CC_clear_cursors(conn, TRUE); + CC_discard_marked_plans(conn); +} - /* - * Now get the authentication request from backend - */ +char +CC_send_settings(ConnectionClass *self) +{ + /* char ini_query[MAX_MESSAGE_LEN]; */ + ConnInfo *ci = &(self->connInfo); - if (!PROTOCOL_62(ci)) - { - BOOL before_64 = PG_VERSION_LT(self, 6.4), - ReadyForQuery = FALSE; +/* QResultClass *res; */ + HSTMT hstmt; + StatementClass *stmt; + RETCODE result; + char status = TRUE; + char *cs, + *ptr; +#ifdef HAVE_STRTOK_R + char *last; +#endif /* HAVE_STRTOK_R */ + CSTR func = "CC_send_settings"; - do - { - if (password_req != AUTH_REQ_OK) - beresp = 'R'; - else - { - beresp = SOCK_get_char(sock); - mylog("auth got '%c'\n", beresp); - } - switch (beresp) - { - case 'E': + mylog("%s: entering...\n", func); - SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); - CC_set_error(self, CONN_INVALID_AUTHENTICATION, msgbuffer); - qlog("ERROR from backend during authentication: '%s'\n", msgbuffer); - if (strncmp(msgbuffer, "Unsupported frontend protocol", 29) == 0) - { /* retry older version */ - if (PROTOCOL_63(ci)) - strcpy(ci->protocol, PG62); - else - strcpy(ci->protocol, PG63); - SOCK_Destructor(sock); - self->sock = (SocketClass *) 0; - CC_initialize_pg_version(self); - goto another_version_retry; - } +/* + * This function must use the local odbc API functions since the odbc state + * has not transitioned to "connected" yet. + */ - return 0; - case 'R': + result = PGAPI_AllocStmt(self, &hstmt); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + return FALSE; + stmt = (StatementClass *) hstmt; - if (password_req != AUTH_REQ_OK) - { - mylog("in 'R' password_req=%s\n", ci->password); - areq = password_req; - if (salt_para) - memcpy(salt, salt_para, sizeof(salt)); - password_req = AUTH_REQ_OK; - } - else - { + stmt->internal = TRUE; /* ensure no BEGIN/COMMIT/ABORT stuff */ - areq = SOCK_get_int(sock, 4); - if (areq == AUTH_REQ_MD5) - SOCK_get_n_char(sock, salt, 4); - else if (areq == AUTH_REQ_CRYPT) - SOCK_get_n_char(sock, salt, 2); + /* Set the Datestyle to the format the driver expects it to be in */ + result = PGAPI_ExecDirect(hstmt, "set DateStyle to 'ISO'", SQL_NTS, 0); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + status = FALSE; - mylog("areq = %d\n", areq); - } - switch (areq) - { - case AUTH_REQ_OK: - break; + mylog("%s: result %d, status %d from set DateStyle\n", func, result, status); - case AUTH_REQ_KRB4: - CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Kerberos 4 authentication not supported"); - return 0; + /* Disable genetic optimizer based on global flag */ + if (ci->drivers.disable_optimizer) + { + result = PGAPI_ExecDirect(hstmt, "set geqo to 'OFF'", SQL_NTS, 0); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + status = FALSE; - case AUTH_REQ_KRB5: - CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Kerberos 5 authentication not supported"); - return 0; + mylog("%s: result %d, status %d from set geqo\n", func, result, status); - case AUTH_REQ_PASSWORD: - mylog("in AUTH_REQ_PASSWORD\n"); + } - if (ci->password[0] == '\0') - { - CC_set_error(self, CONNECTION_NEED_PASSWORD, "A password is required for this connection."); - return -areq; /* need password */ - } + /* KSQO (not applicable to 7.1+ - DJP 21/06/2002) */ + if (ci->drivers.ksqo && PG_VERSION_LT(self, 7.1)) + { + result = PGAPI_ExecDirect(hstmt, "set ksqo to 'ON'", SQL_NTS, 0); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + status = FALSE; - mylog("past need password\n"); + mylog("%s: result %d, status %d from set ksqo\n", func, result, status); - SOCK_put_int(sock, 4 + strlen(ci->password) + 1, 4); - SOCK_put_n_char(sock, ci->password, strlen(ci->password) + 1); - SOCK_flush_output(sock); + } - mylog("past flush\n"); - break; + /* extra_float_digits (applicable since 7.4) */ + if (PG_VERSION_GT(self, 7.3)) + { + result = PGAPI_ExecDirect(hstmt, "set extra_float_digits to 2", SQL_NTS, 0); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + status = FALSE; - case AUTH_REQ_CRYPT: - CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Password crypt authentication not supported"); - return 0; - case AUTH_REQ_MD5: - mylog("in AUTH_REQ_MD5\n"); - if (ci->password[0] == '\0') - { - CC_set_error(self, CONNECTION_NEED_PASSWORD, "A password is required for this connection."); - if (salt_para) - memcpy(salt_para, salt, sizeof(salt)); - return -areq; /* need password */ - } - if (md5_auth_send(self, salt)) - { - CC_set_error(self, CONN_INVALID_AUTHENTICATION, "md5 hashing failed"); - return 0; - } - break; + mylog("%s: result %d, status %d from set extra_float_digits\n", func, result, status); - case AUTH_REQ_SCM_CREDS: - CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Unix socket credential authentication not supported"); - return 0; + } - default: - CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Unknown authentication type"); - return 0; - } - break; - case 'K': /* Secret key (6.4 protocol) */ - self->be_pid = SOCK_get_int(sock, 4); /* pid */ - self->be_key = SOCK_get_int(sock, 4); /* key */ + /* Global settings */ + if (ci->drivers.conn_settings[0] != '\0') + { + cs = strdup(ci->drivers.conn_settings); +#ifdef HAVE_STRTOK_R + ptr = strtok_r(cs, ";", &last); +#else + ptr = strtok(cs, ";"); +#endif /* HAVE_STRTOK_R */ + while (ptr) + { + result = PGAPI_ExecDirect(hstmt, ptr, SQL_NTS, 0); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + status = FALSE; - break; - case 'Z': /* Backend is ready for new query (6.4) */ - ReadyForQuery = TRUE; - break; - case 'N': /* Notices may come */ - while (SOCK_get_string(sock, notice, sizeof(notice) - 1)) ; - break; - default: - CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Unexpected protocol character during authentication"); - return 0; - } + mylog("%s: result %d, status %d from '%s'\n", func, result, status, ptr); - /* - * There were no ReadyForQuery responce before 6.4. - */ - if (before_64 && areq == AUTH_REQ_OK) - ReadyForQuery = TRUE; - } while (!ReadyForQuery); +#ifdef HAVE_STRTOK_R + ptr = strtok_r(NULL, ";", &last); +#else + ptr = strtok(NULL, ";"); +#endif /* HAVE_STRTOK_R */ + } + + free(cs); } + /* Per Datasource settings */ + if (ci->conn_settings[0] != '\0') + { + cs = strdup(ci->conn_settings); +#ifdef HAVE_STRTOK_R + ptr = strtok_r(cs, ";", &last); +#else + ptr = strtok(cs, ";"); +#endif /* HAVE_STRTOK_R */ + while (ptr) + { + result = PGAPI_ExecDirect(hstmt, ptr, SQL_NTS, 0); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + status = FALSE; - CC_clear_error(self); /* clear any password error */ + mylog("%s: result %d, status %d from '%s'\n", func, result, status, ptr); - /* - * send an empty query in order to find out whether the specified - * database really exists on the server machine - */ - mylog("sending an empty query...\n"); +#ifdef HAVE_STRTOK_R + ptr = strtok_r(NULL, ";", &last); +#else + ptr = strtok(NULL, ";"); +#endif /* HAVE_STRTOK_R */ + } - res = CC_send_query(self, " ", NULL, CLEAR_RESULT_ON_ABORT); - if (res == NULL || - (QR_get_status(res) != PGRES_EMPTY_QUERY && - QR_command_nonfatal(res))) - { - mylog("got no result from the empty query. (probably database does not exist)\n"); - CC_set_error(self, CONNECTION_NO_SUCH_DATABASE, "The database does not exist on the server\nor user authentication failed."); - if (res != NULL) - QR_Destructor(res); - return 0; + free(cs); } - if (res) - QR_Destructor(res); - mylog("empty query seems to be OK.\n"); - CC_set_translation(self); + PGAPI_FreeStmt(hstmt, SQL_DROP); - /* - * Send any initial settings - */ + return status; +} - /* - * Get the version number first so we can check it before sending options - * that are now obsolete. DJP 21/06/2002 - */ - CC_lookup_pg_version(self); /* Get PostgreSQL version for - SQLGetInfo use */ - /* - * Since these functions allocate statements, and since the connection - * is not established yet, it would violate odbc state transition - * rules. Therefore, these functions call the corresponding local - * function instead. - */ - CC_send_settings(self); - CC_clear_error(self); /* clear any error */ - CC_lookup_lo(self); /* a hack to get the oid of - our large object oid type */ +/* + * This function is just a hack to get the oid of our Large Object oid type. + * If a real Large Object oid type is made part of Postgres, this function + * will go away and the define 'PG_TYPE_LO' will be updated. + */ +void +CC_lookup_lo(ConnectionClass *self) +{ + HSTMT hstmt; + StatementClass *stmt; + RETCODE result; + CSTR func = "CC_lookup_lo"; - /* - * Multibyte handling is available ? - */ - if (PG_VERSION_GE(self, 6.4)) + mylog("%s: entering...\n", func); + +/* + * This function must use the local odbc API functions since the odbc state + * has not transitioned to "connected" yet. + */ + result = PGAPI_AllocStmt(self, &hstmt); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + return; + stmt = (StatementClass *) hstmt; + + result = PGAPI_ExecDirect(hstmt, "select oid from pg_type where typname='" PG_TYPE_LO_NAME "'", SQL_NTS, 0); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { - CC_lookup_characterset(self); - if (CC_get_errornumber(self) != 0) - return 0; - - if (self->unicode) - { - if (!self->client_encoding || - !stricmp(self->client_encoding, "UNICODE")) - { - QResultClass *res; - if (PG_VERSION_LT(self, 7.1)) - { - CC_set_error(self, CONN_NOT_IMPLEMENTED_ERROR, "UTF-8 conversion isn't implemented before 7.1"); - return 0; - } - if (self->client_encoding) - free(self->client_encoding); - self->client_encoding = NULL; - if (res = CC_send_query(self, "set client_encoding to 'UTF8'", NULL, CLEAR_RESULT_ON_ABORT), res) - { - self->client_encoding = strdup("UNICODE"); - self->ccsc = pg_CS_code(self->client_encoding); - QR_Destructor(res); - - } - } - } + PGAPI_FreeStmt(hstmt, SQL_DROP); + return; } - else if (self->unicode) + + result = PGAPI_Fetch(hstmt); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { - CC_set_error(self, CONN_NOT_IMPLEMENTED_ERROR, "Unicode isn't supported before 6.4"); - return 0; + PGAPI_FreeStmt(hstmt, SQL_DROP); + return; } - ci->updatable_cursors = 0; -#ifdef DRIVER_CURSOR_IMPLEMENT - if (!ci->drivers.use_declarefetch && - PG_VERSION_GE(self, 7.0)) /* Tid scan since 7.0 */ - ci->updatable_cursors = ci->allow_keyset; -#endif /* DRIVER_CURSOR_IMPLEMENT */ - - CC_clear_error(self); /* clear any initial command errors */ - self->status = CONN_CONNECTED; - - mylog("%s: returning...\n", func); - - return 1; - -} - - -char -CC_add_statement(ConnectionClass *self, StatementClass *stmt) -{ - int i; - - mylog("CC_add_statement: self=%u, stmt=%u\n", self, stmt); - - for (i = 0; i < self->num_stmts; i++) + result = PGAPI_GetData(hstmt, 1, SQL_C_SLONG, &self->lobj_type, sizeof(self->lobj_type), NULL); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { - if (!self->stmts[i]) - { - stmt->hdbc = self; - self->stmts[i] = stmt; - return TRUE; - } + PGAPI_FreeStmt(hstmt, SQL_DROP); + return; } - /* no more room -- allocate more memory */ - self->stmts = (StatementClass **) realloc(self->stmts, sizeof(StatementClass *) * (STMT_INCREMENT + self->num_stmts)); - if (!self->stmts) - return FALSE; - - memset(&self->stmts[self->num_stmts], 0, sizeof(StatementClass *) * STMT_INCREMENT); - - stmt->hdbc = self; - self->stmts[self->num_stmts] = stmt; - - self->num_stmts += STMT_INCREMENT; + mylog("Got the large object oid: %d\n", self->lobj_type); + qlog(" [ Large Object oid = %d ]\n", self->lobj_type); - return TRUE; + result = PGAPI_FreeStmt(hstmt, SQL_DROP); } -char -CC_remove_statement(ConnectionClass *self, StatementClass *stmt) +/* + * This function initializes the version of PostgreSQL from + * connInfo.protocol that we're connected to. + * h-inoue 01-2-2001 + */ +void +CC_initialize_pg_version(ConnectionClass *self) { - int i; - - if (stmt->status == STMT_EXECUTING) - return FALSE; - for (i = 0; i < self->num_stmts; i++) + strcpy(self->pg_version, self->connInfo.protocol); + if (PROTOCOL_62(&self->connInfo)) { - if (self->stmts[i] == stmt) - { - self->stmts[i] = NULL; - return TRUE; - } + self->pg_version_number = (float) 6.2; + self->pg_version_major = 6; + self->pg_version_minor = 2; + } + else if (PROTOCOL_63(&self->connInfo)) + { + self->pg_version_number = (float) 6.3; + self->pg_version_major = 6; + self->pg_version_minor = 3; + } + else + { + self->pg_version_number = (float) 6.4; + self->pg_version_major = 6; + self->pg_version_minor = 4; } - - return FALSE; } /* - * Create a more informative error message by concatenating the connection - * error message with its socket error message. + * This function gets the version of PostgreSQL that we're connected to. + * This is used to return the correct info in SQLGetInfo + * DJP - 25-1-2001 */ -char * -CC_create_errormsg(ConnectionClass *self) +void +CC_lookup_pg_version(ConnectionClass *self) { - SocketClass *sock = self->sock; - int pos; - char msg[4096]; + HSTMT hstmt; + StatementClass *stmt; + RETCODE result; + char szVersion[32]; + int major, + minor; + CSTR func = "CC_lookup_pg_version"; - mylog("enter CC_create_errormsg\n"); + mylog("%s: entering...\n", func); - msg[0] = '\0'; +/* + * This function must use the local odbc API functions since the odbc state + * has not transitioned to "connected" yet. + */ + result = PGAPI_AllocStmt(self, &hstmt); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + return; + stmt = (StatementClass *) hstmt; - if (CC_get_errormsg(self)) - strncpy(msg, CC_get_errormsg(self), sizeof(msg)); + /* get the server's version if possible */ + result = PGAPI_ExecDirect(hstmt, "select version()", SQL_NTS, 0); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + { + PGAPI_FreeStmt(hstmt, SQL_DROP); + return; + } - mylog("msg = '%s'\n", msg); + result = PGAPI_Fetch(hstmt); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + { + PGAPI_FreeStmt(hstmt, SQL_DROP); + return; + } - if (sock && sock->errormsg && sock->errormsg[0] != '\0') + result = PGAPI_GetData(hstmt, 1, SQL_C_CHAR, self->pg_version, MAX_INFO_STRING, NULL); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) { - pos = strlen(msg); - sprintf(&msg[pos], ";\n%s", sock->errormsg); + PGAPI_FreeStmt(hstmt, SQL_DROP); + return; } - mylog("exit CC_create_errormsg\n"); - return msg ? strdup(msg) : NULL; -} + /* + * Extract the Major and Minor numbers from the string. This assumes + * the string starts 'Postgresql X.X' + */ + strcpy(szVersion, "0.0"); + if (sscanf(self->pg_version, "%*s %d.%d", &major, &minor) >= 2) + { + sprintf(szVersion, "%d.%d", major, minor); + self->pg_version_major = major; + self->pg_version_minor = minor; + } + self->pg_version_number = (float) atof(szVersion); + if (PG_VERSION_GE(self, 7.3)) + self->schema_support = 1; + mylog("Got the PostgreSQL version string: '%s'\n", self->pg_version); + mylog("Extracted PostgreSQL version number: '%1.1f'\n", self->pg_version_number); + qlog(" [ PostgreSQL version string = '%s' ]\n", self->pg_version); + qlog(" [ PostgreSQL version number = '%1.1f' ]\n", self->pg_version_number); -void -CC_set_error(ConnectionClass *self, int number, const char *message) -{ - if (self->__error_message) - free(self->__error_message); - self->__error_number = number; - self->__error_message = message ? strdup(message) : NULL; + result = PGAPI_FreeStmt(hstmt, SQL_DROP); } - -void -CC_set_errormsg(ConnectionClass *self, const char *message) +int +CC_get_max_query_len(const ConnectionClass *conn) { - if (self->__error_message) - free(self->__error_message); - self->__error_message = message ? strdup(message) : NULL; -} - + int value; -char -CC_get_error(ConnectionClass *self, int *number, char **message) -{ - int rv; - char *msgcrt; - - mylog("enter CC_get_error\n"); - - /* Create a very informative errormsg if it hasn't been done yet. */ - if (!self->errormsg_created) - { - msgcrt = CC_create_errormsg(self); - if (self->__error_message) - free(self->__error_message); - self->__error_message = msgcrt; - self->errormsg_created = TRUE; - } - - if (CC_get_errornumber(self)) - { - *number = CC_get_errornumber(self); - *message = CC_get_errormsg(self); - } - rv = (CC_get_errornumber(self) != 0); - - self->__error_number = 0; /* clear the error */ - - mylog("exit CC_get_error\n"); - - return rv; + /* Long Queries in 7.0+ */ + if (PG_VERSION_GE(conn, 7.0)) + value = 0 /* MAX_STATEMENT_LEN */ ; + /* Prior to 7.0 we used 2*BLCKSZ */ + else if (PG_VERSION_GE(conn, 6.5)) + value = (2 * BLCKSZ); + else + /* Prior to 6.5 we used BLCKSZ */ + value = BLCKSZ; + return value; } -static void CC_clear_cursors(ConnectionClass *self, BOOL allcursors) +/* + * This doesn't really return the CURRENT SCHEMA + * but there's no alternative. + */ +const char * +CC_get_current_schema(ConnectionClass *conn) { - int i; - StatementClass *stmt; - QResultClass *res; - - for (i = 0; i < self->num_stmts; i++) + if (!conn->current_schema && conn->schema_support) { - stmt = self->stmts[i]; - if (stmt && (res = SC_get_Result(stmt)) && - res->cursor && res->cursor[0]) + QResultClass *res; + + if (res = CC_send_query(conn, "select current_schema()", NULL, CLEAR_RESULT_ON_ABORT), res) { - /* - * non-holdable cursors are automatically closed - * at commit time. - * all cursors are automatically closed - * at rollback time. - */ - if (res->cursor) - { - free(res->cursor); - res->cursor = NULL; - } + if (QR_get_num_total_tuples(res) == 1) + conn->current_schema = strdup(QR_get_value_backend_row(res, 0, 0)); + QR_Destructor(res); } } + return (const char *) conn->current_schema; } -void CC_on_commit(ConnectionClass *conn) +int CC_mark_a_plan_to_discard(ConnectionClass *conn, const char *plan) { - if (CC_is_in_trans(conn)) - { -#ifdef DRIVER_CURSOR_IMPLEMENT - if (conn->result_uncommitted) - ProcessRollback(conn, FALSE); -#endif /* DRIVER_CURSOR_IMPLEMENT */ - CC_set_no_trans(conn); - CC_set_no_manual_trans(conn); - } - conn->result_uncommitted = 0; - CC_clear_cursors(conn, TRUE); - CC_discard_marked_plans(conn); + int cnt = conn->num_discardp + 1; + char *pname; + + CC_REALLOC_return_with_error(conn->discardp, char *, + (cnt * sizeof(char *)), conn, "Couldn't alloc discardp.", -1) + CC_MALLOC_return_with_error(pname, char, (strlen(plan) + 1), + conn, "Couldn't alloc discardp mem.", -1) + strcpy(pname, plan); + conn->discardp[conn->num_discardp++] = pname; + return 1; } -void CC_on_abort(ConnectionClass *conn, UDWORD opt) +int CC_discard_marked_plans(ConnectionClass *conn) { - BOOL set_no_trans = FALSE; + int i, cnt; + QResultClass *res; + char cmd[32]; - if (0 != (opt & CONN_DEAD)) - opt |= NO_TRANS; - if (CC_is_in_trans(conn)) - { -#ifdef DRIVER_CURSOR_IMPLEMENT - if (conn->result_uncommitted) - ProcessRollback(conn, TRUE); -#endif /* DRIVER_CURSOR_IMPLEMENT */ - if (0 != (opt & NO_TRANS)) - { - CC_set_no_trans(conn); - CC_set_no_manual_trans(conn); - set_no_trans = TRUE; - } - } - CC_clear_cursors(conn, TRUE); - if (0 != (opt & CONN_DEAD)) + if ((cnt = conn->num_discardp) <= 0) + return 0; + for (i = cnt - 1; i >= 0; i--) { - conn->status = CONN_DOWN; - if (conn->sock) + sprintf(cmd, "DEALLOCATE \"%s\"", conn->discardp[i]); + res = CC_send_query(conn, cmd, NULL, CLEAR_RESULT_ON_ABORT); + if (res) { - SOCK_Destructor(conn->sock); - conn->sock = NULL; + QR_Destructor(res); + free(conn->discardp[i]); + conn->num_discardp--; } + else + return -1; } - else if (set_no_trans) - CC_discard_marked_plans(conn); - conn->result_uncommitted = 0; + return 1; } + +#ifndef USE_LIBPQ /* - * The "result_in" is only used by QR_next_tuple() to fetch another group of rows into - * the same existing QResultClass (this occurs when the tuple cache is depleted and - * needs to be re-filled). - * - * The "cursor" is used by SQLExecute to associate a statement handle as the cursor name - * (i.e., C3326857) for SQL select statements. This cursor is then used in future - * 'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements. + * IMPLEMENTATION CONNECTION CLASS */ -QResultClass * -CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) +ConnectionClass * +CC_Constructor() { - QResultClass *cmdres = NULL, - *retres = NULL, - *res = NULL; - BOOL clear_result_on_abort = ((flag & CLEAR_RESULT_ON_ABORT) != 0), - create_keyset = ((flag & CREATE_KEYSET) != 0), - issue_begin = ((flag & GO_INTO_TRANSACTION) != 0 && !CC_is_in_trans(self)); - char swallow, *wq, *ptr; - int id; - SocketClass *sock = self->sock; - int maxlen, - empty_reqs; - BOOL msg_truncated, - ReadyToReturn = FALSE, - query_completed = FALSE, - before_64 = PG_VERSION_LT(self, 6.4), - aborted = FALSE, - used_passed_result_object = FALSE; - UDWORD abort_opt; - int func_cs_count = 0; - - /* ERROR_MSG_LENGTH is suffcient */ - char msgbuffer[ERROR_MSG_LENGTH + 1]; - - /* QR_set_command() dups this string so doesn't need static */ - char cmdbuffer[ERROR_MSG_LENGTH + 1]; + ConnectionClass *rv; - mylog("send_query(): conn=%u, query='%s'\n", self, query); - qlog("conn=%u, query='%s'\n", self, query); + rv = (ConnectionClass *) malloc(sizeof(ConnectionClass)); - if (!self->sock) + if (rv != NULL) { - CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send Query(connection dead)"); - CC_on_abort(self, NO_TRANS); - return NULL; - } + rv->henv = NULL; /* not yet associated with an environment */ - /* Indicate that we are sending a query to the backend */ - maxlen = CC_get_max_query_len(self); - if (maxlen > 0 && maxlen < (int) strlen(query) + 1) - { - CC_set_error(self, CONNECTION_MSG_TOO_LONG, "Query string is too long"); - return NULL; - } + rv->__error_message = NULL; + rv->__error_number = 0; + rv->errormsg_created = FALSE; - if ((NULL == query) || (query[0] == '\0')) - return NULL; + rv->status = CONN_NOT_CONNECTED; + rv->transact_status = CONN_IN_AUTOCOMMIT; /* autocommit by default */ - if (SOCK_get_errcode(sock) != 0) - { - CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send Query to backend"); - CC_on_abort(self, NO_TRANS | CONN_DEAD); - return NULL; - } + CC_conninfo_init(&(rv->connInfo)); + rv->sock = SOCK_Constructor(rv); + if (!rv->sock) + return NULL; -#define return DONT_CALL_RETURN_FROM_HERE??? - ENTER_INNER_CONN_CS(self, func_cs_count); - SOCK_put_char(sock, 'Q'); - if (SOCK_get_errcode(sock) != 0) - { - CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send Query to backend"); - CC_on_abort(self, NO_TRANS | CONN_DEAD); - goto cleanup; - } + rv->stmts = (StatementClass **) malloc(sizeof(StatementClass *) * STMT_INCREMENT); + if (!rv->stmts) + return NULL; + memset(rv->stmts, 0, sizeof(StatementClass *) * STMT_INCREMENT); - if (issue_begin) - SOCK_put_n_char(sock, "BEGIN;", 6); - SOCK_put_string(sock, query); - SOCK_flush_output(sock); + rv->num_stmts = STMT_INCREMENT; + rv->descs = (DescriptorClass **) malloc(sizeof(DescriptorClass *) * STMT_INCREMENT); + if (!rv->descs) + return NULL; + memset(rv->descs, 0, sizeof(DescriptorClass *) * STMT_INCREMENT); - if (SOCK_get_errcode(sock) != 0) - { - CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send Query to backend"); - CC_on_abort(self, NO_TRANS | CONN_DEAD); - goto cleanup; - } + rv->num_descs = STMT_INCREMENT; - mylog("send_query: done sending query\n"); + rv->lobj_type = PG_TYPE_LO_UNDEFINED; - empty_reqs = 0; - for (wq = query; isspace((UCHAR) *wq); wq++) - ; - if (*wq == '\0') - empty_reqs = 1; - cmdres = qi ? qi->result_in : NULL; - if (cmdres) - used_passed_result_object = TRUE; - else - { - cmdres = QR_Constructor(); - if (!cmdres) - { - CC_set_error(self, CONNECTION_COULD_NOT_RECEIVE, "Could not create result info in send_query."); + rv->ntables = 0; + rv->col_info = NULL; + + rv->translation_option = 0; + rv->translation_handle = NULL; + rv->DataSourceToDriver = NULL; + rv->DriverToDataSource = NULL; + rv->driver_version = ODBCVER; + memset(rv->pg_version, 0, sizeof(rv->pg_version)); + rv->pg_version_number = .0; + rv->pg_version_major = 0; + rv->pg_version_minor = 0; + rv->ms_jet = 0; + rv->unicode = 0; + rv->result_uncommitted = 0; + rv->schema_support = 0; + rv->isolation = SQL_TXN_READ_COMMITTED; + rv->client_encoding = NULL; + rv->server_encoding = NULL; + rv->current_schema = NULL; + rv->num_discardp = 0; + rv->discardp = NULL; + + /* Initialize statement options to defaults */ + /* Statements under this conn will inherit these options */ + + InitializeStatementOptions(&rv->stmtOptions); + InitializeARDFields(&rv->ardOptions); + InitializeAPDFields(&rv->apdOptions); + INIT_CONN_CS(rv); + } + return rv; +} + + +char +CC_Destructor(ConnectionClass *self) +{ + mylog("enter CC_Destructor, self=%u\n", self); + + if (self->status == CONN_EXECUTING) + return 0; + + CC_cleanup(self); /* cleanup socket and statements */ + + mylog("after CC_Cleanup\n"); + + /* Free up statement holders */ + if (self->stmts) + { + free(self->stmts); + self->stmts = NULL; + } + + if (self->descs) + { + free(self->descs); + self->descs = NULL; + } + + mylog("after free statement holders\n"); + + if (self->__error_message) + free(self->__error_message); + DELETE_CONN_CS(self); + free(self); + + mylog("exit CC_Destructor\n"); + + return 1; +} + + +/* This is called by SQLDisconnect also */ +char +CC_cleanup(ConnectionClass *self) +{ + int i; + StatementClass *stmt; + DescriptorClass *desc; + + if (self->status == CONN_EXECUTING) + return FALSE; + + mylog("in CC_Cleanup, self=%u\n", self); + + /* Cancel an ongoing transaction */ + /* We are always in the middle of a transaction, */ + /* even if we are in auto commit. */ + if (self->sock) + { + CC_abort(self); + + mylog("after CC_abort\n"); + + /* This actually closes the connection to the dbase */ + SOCK_Destructor(self->sock); + self->sock = NULL; + } + + mylog("after SOCK destructor\n"); + + /* Free all the stmts on this connection */ + for (i = 0; i < self->num_stmts; i++) + { + stmt = self->stmts[i]; + if (stmt) + { + stmt->hdbc = NULL; /* prevent any more dbase interactions */ + + SC_Destructor(stmt); + + self->stmts[i] = NULL; + } + } + + /* Free all the descs on this connection */ + for (i = 0; i < self->num_descs; i++) + { + desc = self->descs[i]; + if (desc) + { + DC_get_conn(desc) = NULL; /* prevent any more dbase interactions */ + DC_Destructor(desc); + free(desc); + self->descs[i] = NULL; + } + } + + /* Check for translation dll */ +#ifdef WIN32 + if (self->translation_handle) + { + FreeLibrary(self->translation_handle); + self->translation_handle = NULL; + } +#endif + + self->status = CONN_NOT_CONNECTED; + self->transact_status = CONN_IN_AUTOCOMMIT; + CC_conninfo_init(&(self->connInfo)); + if (self->client_encoding) + { + free(self->client_encoding); + self->client_encoding = NULL; + } + if (self->server_encoding) + { + free(self->server_encoding); + self->server_encoding = NULL; + } + if (self->current_schema) + { + free(self->current_schema); + self->current_schema = NULL; + } + /* Free cached table info */ + if (self->col_info) + { + for (i = 0; i < self->ntables; i++) + { + if (self->col_info[i]->result) /* Free the SQLColumns result structure */ + QR_Destructor(self->col_info[i]->result); + + if (self->col_info[i]->schema) + free(self->col_info[i]->schema); + free(self->col_info[i]); + } + free(self->col_info); + self->col_info = NULL; + } + self->ntables = 0; + if (self->num_discardp > 0 && self->discardp) + { + for (i = 0; i < self->num_discardp; i++) + free(self->discardp[i]); + self->num_discardp = 0; + } + if (self->discardp) + { + free(self->discardp); + self->discardp = NULL; + } + + mylog("exit CC_Cleanup\n"); + return TRUE; +} + +static int +md5_auth_send(ConnectionClass *self, const char *salt) +{ + char *pwd1 = NULL, *pwd2 = NULL; + ConnInfo *ci = &(self->connInfo); + SocketClass *sock = self->sock; + + if (!(pwd1 = malloc(MD5_PASSWD_LEN + 1))) + return 1; + if (!EncryptMD5(ci->password, ci->username, strlen(ci->username), pwd1)) + { + free(pwd1); + return 1; + } + if (!(pwd2 = malloc(MD5_PASSWD_LEN + 1))) + { + free(pwd1); + return 1; + } + if (!EncryptMD5(pwd1 + strlen("md5"), salt, 4, pwd2)) + { + free(pwd2); + free(pwd1); + return 1; + } + free(pwd1); + SOCK_put_int(sock, 4 + strlen(pwd2) + 1, 4); + SOCK_put_n_char(sock, pwd2, strlen(pwd2) + 1); + SOCK_flush_output(sock); + free(pwd2); + return 0; +} + + +char +CC_connect(ConnectionClass *self, char password_req, char *salt_para) +{ + StartupPacket sp; + StartupPacket6_2 sp62; + QResultClass *res; + SocketClass *sock; + ConnInfo *ci = &(self->connInfo); + int areq = -1; + int beresp; + char msgbuffer[ERROR_MSG_LENGTH]; + char salt[5], notice[512]; + CSTR func = "CC_connect"; + char *encoding; + + mylog("%s: entering...\n", func); + + if (password_req != AUTH_REQ_OK) + + sock = self->sock; /* already connected, just authenticate */ + + else + { + qlog("Global Options: Version='%s', fetch=%d, socket=%d, unknown_sizes=%d, max_varchar_size=%d, max_longvarchar_size=%d\n", + POSTGRESDRIVERVERSION, + ci->drivers.fetch_max, + ci->drivers.socket_buffersize, + ci->drivers.unknown_sizes, + ci->drivers.max_varchar_size, + ci->drivers.max_longvarchar_size); + qlog(" disable_optimizer=%d, ksqo=%d, unique_index=%d, use_declarefetch=%d\n", + ci->drivers.disable_optimizer, + ci->drivers.ksqo, + ci->drivers.unique_index, + ci->drivers.use_declarefetch); + qlog(" text_as_longvarchar=%d, unknowns_as_longvarchar=%d, bools_as_char=%d NAMEDATALEN=%d\n", + ci->drivers.text_as_longvarchar, + ci->drivers.unknowns_as_longvarchar, + ci->drivers.bools_as_char, + TABLE_NAME_STORAGE_LEN); + + encoding = check_client_encoding(ci->conn_settings); + if (encoding && strcmp(encoding, "OTHER")) + self->client_encoding = strdup(encoding); + else + { + encoding = check_client_encoding(ci->drivers.conn_settings); + if (encoding && strcmp(encoding, "OTHER")) + self->client_encoding = strdup(encoding); + } + if (self->client_encoding) + self->ccsc = pg_CS_code(self->client_encoding); + qlog(" extra_systable_prefixes='%s', conn_settings='%s' conn_encoding='%s'\n", + ci->drivers.extra_systable_prefixes, + ci->drivers.conn_settings, + encoding ? encoding : ""); + + if (self->status != CONN_NOT_CONNECTED) + { + CC_set_error(self, CONN_OPENDB_ERROR, "Already connected."); + return 0; + } + + if (ci->port[0] == '\0' || +#ifdef WIN32 + ci->server[0] == '\0' || +#endif /* WIN32 */ + ci->database[0] == '\0') + { + CC_set_error(self, CONN_INIREAD_ERROR, "Missing server name, port, or database name in call to CC_connect."); + return 0; + } + + mylog("CC_connect(): DSN = '%s', server = '%s', port = '%s'," +#ifdef HAVE_UNIX_SOCKETS + " uds = '%s'," +#endif + " database = '%s', username = '%s'," + " password='%s'\n", ci->dsn, ci->server, ci->port, +#ifdef HAVE_UNIX_SOCKETS + ci->uds, +#endif + ci->database, ci->username, ci->password ? "xxxxx" : ""); + +another_version_retry: + + /* + * If the socket was closed for some reason (like a SQLDisconnect, + * but no SQLFreeConnect then create a socket now. + */ + if (!self->sock) + { + self->sock = SOCK_Constructor(self); + if (!self->sock) + { + CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Could not open a socket to the server"); + return 0; + } + } + + sock = self->sock; + + mylog("connecting to the server socket...\n"); + + SOCK_connect_to(sock, (short) atoi(ci->port), ci->server +#ifdef HAVE_UNIX_SOCKETS + , ci->uds +#endif + ); + if (SOCK_get_errcode(sock) != 0) + { + mylog("connection to the server socket failed.\n"); + CC_set_error(self, CONNECTION_SERVER_NOT_REACHED, "Could not connect to the server"); + return 0; + } + mylog("connection to the server socket succeeded.\n"); + + if (PROTOCOL_62(ci)) + { + sock->reverse = TRUE; /* make put_int and get_int work + * for 6.2 */ + + memset(&sp62, 0, sizeof(StartupPacket6_2)); + SOCK_put_int(sock, htonl(4 + sizeof(StartupPacket6_2)), 4); + sp62.authtype = htonl(NO_AUTHENTICATION); + strncpy(sp62.database, ci->database, PATH_SIZE); + strncpy(sp62.user, ci->username, USRNAMEDATALEN); + SOCK_put_n_char(sock, (char *) &sp62, sizeof(StartupPacket6_2)); + SOCK_flush_output(sock); + } + else + { + memset(&sp, 0, sizeof(StartupPacket)); + + mylog("sizeof startup packet = %d\n", sizeof(StartupPacket)); + + /* Send length of Authentication Block */ + SOCK_put_int(sock, 4 + sizeof(StartupPacket), 4); + + if (PROTOCOL_63(ci)) + sp.protoVersion = (ProtocolVersion) htonl(PG_PROTOCOL_63); + else + sp.protoVersion = (ProtocolVersion) htonl(PG_PROTOCOL_LATEST); + + strncpy(sp.database, ci->database, SM_DATABASE); + strncpy(sp.user, ci->username, SM_USER); + + SOCK_put_n_char(sock, (char *) &sp, sizeof(StartupPacket)); + SOCK_flush_output(sock); + } + + mylog("sent the authentication block.\n"); + + if (sock->errornumber != 0) + { + mylog("couldn't send the authentication block properly.\n"); + CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Sending the authentication packet failed"); + return 0; + } + mylog("sent the authentication block successfully.\n"); + } + + + mylog("gonna do authentication\n"); + + + /* + * Now get the authentication request from backend + */ + + if (!PROTOCOL_62(ci)) + { + BOOL before_64 = PG_VERSION_LT(self, 6.4), + ReadyForQuery = FALSE; + + do + { + if (password_req != AUTH_REQ_OK) + beresp = 'R'; + else + { + beresp = SOCK_get_char(sock); + mylog("auth got '%c'\n", beresp); + } + + switch (beresp) + { + case 'E': + + SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); + CC_set_error(self, CONN_INVALID_AUTHENTICATION, msgbuffer); + qlog("ERROR from backend during authentication: '%s'\n", msgbuffer); + if (strncmp(msgbuffer, "Unsupported frontend protocol", 29) == 0) + { /* retry older version */ + if (PROTOCOL_63(ci)) + strcpy(ci->protocol, PG62); + else + strcpy(ci->protocol, PG63); + SOCK_Destructor(sock); + self->sock = (SocketClass *) 0; + CC_initialize_pg_version(self); + goto another_version_retry; + } + + return 0; + case 'R': + + if (password_req != AUTH_REQ_OK) + { + mylog("in 'R' password_req=%s\n", ci->password); + areq = password_req; + if (salt_para) + memcpy(salt, salt_para, sizeof(salt)); + password_req = AUTH_REQ_OK; + } + else + { + + areq = SOCK_get_int(sock, 4); + if (areq == AUTH_REQ_MD5) + SOCK_get_n_char(sock, salt, 4); + else if (areq == AUTH_REQ_CRYPT) + SOCK_get_n_char(sock, salt, 2); + + mylog("areq = %d\n", areq); + } + switch (areq) + { + case AUTH_REQ_OK: + break; + + case AUTH_REQ_KRB4: + CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Kerberos 4 authentication not supported"); + return 0; + + case AUTH_REQ_KRB5: + CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Kerberos 5 authentication not supported"); + return 0; + + case AUTH_REQ_PASSWORD: + mylog("in AUTH_REQ_PASSWORD\n"); + + if (ci->password[0] == '\0') + { + CC_set_error(self, CONNECTION_NEED_PASSWORD, "A password is required for this connection."); + return -areq; /* need password */ + } + + mylog("past need password\n"); + + SOCK_put_int(sock, 4 + strlen(ci->password) + 1, 4); + SOCK_put_n_char(sock, ci->password, strlen(ci->password) + 1); + SOCK_flush_output(sock); + + mylog("past flush\n"); + break; + + case AUTH_REQ_CRYPT: + CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Password crypt authentication not supported"); + return 0; + case AUTH_REQ_MD5: + mylog("in AUTH_REQ_MD5\n"); + if (ci->password[0] == '\0') + { + CC_set_error(self, CONNECTION_NEED_PASSWORD, "A password is required for this connection."); + if (salt_para) + memcpy(salt_para, salt, sizeof(salt)); + return -areq; /* need password */ + } + if (md5_auth_send(self, salt)) + { + CC_set_error(self, CONN_INVALID_AUTHENTICATION, "md5 hashing failed"); + return 0; + } + break; + + case AUTH_REQ_SCM_CREDS: + CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Unix socket credential authentication not supported"); + return 0; + + default: + CC_set_error(self, CONN_AUTH_TYPE_UNSUPPORTED, "Unknown authentication type"); + return 0; + } + break; + case 'K': /* Secret key (6.4 protocol) */ + self->be_pid = SOCK_get_int(sock, 4); /* pid */ + self->be_key = SOCK_get_int(sock, 4); /* key */ + + break; + case 'Z': /* Backend is ready for new query (6.4) */ + ReadyForQuery = TRUE; + break; + case 'N': /* Notices may come */ + while (SOCK_get_string(sock, notice, sizeof(notice) - 1)) ; + break; + default: + CC_set_error(self, CONN_INVALID_AUTHENTICATION, "Unexpected protocol character during authentication"); + return 0; + } + + /* + * There were no ReadyForQuery responce before 6.4. + */ + if (before_64 && areq == AUTH_REQ_OK) + ReadyForQuery = TRUE; + } while (!ReadyForQuery); + } + + + CC_clear_error(self); /* clear any password error */ + + /* + * send an empty query in order to find out whether the specified + * database really exists on the server machine + */ + mylog("sending an empty query...\n"); + + res = CC_send_query(self, " ", NULL, CLEAR_RESULT_ON_ABORT); + if (res == NULL || + (QR_get_status(res) != PGRES_EMPTY_QUERY && + QR_command_nonfatal(res))) + { + mylog("got no result from the empty query. (probably database does not exist)\n"); + CC_set_error(self, CONNECTION_NO_SUCH_DATABASE, "The database does not exist on the server\nor user authentication failed."); + if (res != NULL) + QR_Destructor(res); + return 0; + } + if (res) + QR_Destructor(res); + + mylog("empty query seems to be OK.\n"); + + CC_set_translation(self); + + /* + * Send any initial settings + */ + + /* + * Get the version number first so we can check it before sending options + * that are now obsolete. DJP 21/06/2002 + */ + + CC_lookup_pg_version(self); /* Get PostgreSQL version for + SQLGetInfo use */ + /* + * Since these functions allocate statements, and since the connection + * is not established yet, it would violate odbc state transition + * rules. Therefore, these functions call the corresponding local + * function instead. + */ + CC_send_settings(self); + CC_clear_error(self); /* clear any error */ + CC_lookup_lo(self); /* a hack to get the oid of + our large object oid type */ + + /* + * Multibyte handling is available ? + */ + if (PG_VERSION_GE(self, 6.4)) + { + CC_lookup_characterset(self); + if (CC_get_errornumber(self) != 0) + return 0; + + if (self->unicode) + { + if (!self->client_encoding || + !stricmp(self->client_encoding, "UNICODE")) + { + QResultClass *res; + if (PG_VERSION_LT(self, 7.1)) + { + CC_set_error(self, CONN_NOT_IMPLEMENTED_ERROR, "UTF-8 conversion isn't implemented before 7.1"); + return 0; + } + if (self->client_encoding) + free(self->client_encoding); + self->client_encoding = NULL; + if (res = CC_send_query(self, "set client_encoding to 'UTF8'", NULL, CLEAR_RESULT_ON_ABORT), res) + { + self->client_encoding = strdup("UNICODE"); + self->ccsc = pg_CS_code(self->client_encoding); + QR_Destructor(res); + + } + } + } + } + else if (self->unicode) + { + CC_set_error(self, CONN_NOT_IMPLEMENTED_ERROR, "Unicode isn't supported before 6.4"); + return 0; + } + + ci->updatable_cursors = 0; +#ifdef DRIVER_CURSOR_IMPLEMENT + if (!ci->drivers.use_declarefetch && + PG_VERSION_GE(self, 7.0)) /* Tid scan since 7.0 */ + ci->updatable_cursors = ci->allow_keyset; +#endif /* DRIVER_CURSOR_IMPLEMENT */ + + CC_clear_error(self); /* clear any initial command errors */ + self->status = CONN_CONNECTED; + + mylog("%s: returning...\n", func); + + return 1; + +} + + +/* + * Create a more informative error message by concatenating the connection + * error message with its socket error message. + */ +char * +CC_create_errormsg(ConnectionClass *self) +{ + SocketClass *sock = self->sock; + int pos; + char msg[4096]; + + mylog("enter CC_create_errormsg\n"); + + msg[0] = '\0'; + + if (CC_get_errormsg(self)) + strncpy(msg, CC_get_errormsg(self), sizeof(msg)); + + mylog("msg = '%s'\n", msg); + + if (sock && sock->errormsg && sock->errormsg[0] != '\0') + { + pos = strlen(msg); + sprintf(&msg[pos], ";\n%s", sock->errormsg); + } + + mylog("exit CC_create_errormsg\n"); + return msg ? strdup(msg) : NULL; +} + +void CC_on_abort(ConnectionClass *conn, UDWORD opt) +{ + BOOL set_no_trans = FALSE; + + if (0 != (opt & CONN_DEAD)) + opt |= NO_TRANS; + if (CC_is_in_trans(conn)) + { +#ifdef DRIVER_CURSOR_IMPLEMENT + if (conn->result_uncommitted) + ProcessRollback(conn, TRUE); +#endif /* DRIVER_CURSOR_IMPLEMENT */ + if (0 != (opt & NO_TRANS)) + { + CC_set_no_trans(conn); + CC_set_no_manual_trans(conn); + set_no_trans = TRUE; + } + } + CC_clear_cursors(conn, TRUE); + if (0 != (opt & CONN_DEAD)) + { + conn->status = CONN_DOWN; + if (conn->sock) + { + SOCK_Destructor(conn->sock); + conn->sock = NULL; + } + } + else if (set_no_trans) + CC_discard_marked_plans(conn); + conn->result_uncommitted = 0; +} + +/* + * The "result_in" is only used by QR_next_tuple() to fetch another group of rows into + * the same existing QResultClass (this occurs when the tuple cache is depleted and + * needs to be re-filled). + * + * The "cursor" is used by SQLExecute to associate a statement handle as the cursor name + * (i.e., C3326857) for SQL select statements. This cursor is then used in future + * 'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements. + */ +QResultClass * +CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) +{ + QResultClass *cmdres = NULL, + *retres = NULL, + *res = NULL; + BOOL clear_result_on_abort = ((flag & CLEAR_RESULT_ON_ABORT) != 0), + create_keyset = ((flag & CREATE_KEYSET) != 0), + issue_begin = ((flag & GO_INTO_TRANSACTION) != 0 && !CC_is_in_trans(self)); + char swallow, *wq, *ptr; + int id; + SocketClass *sock = self->sock; + int maxlen, + empty_reqs; + BOOL msg_truncated, + ReadyToReturn = FALSE, + query_completed = FALSE, + before_64 = PG_VERSION_LT(self, 6.4), + aborted = FALSE, + used_passed_result_object = FALSE; + UDWORD abort_opt; + int func_cs_count = 0; + + /* ERROR_MSG_LENGTH is suffcient */ + char msgbuffer[ERROR_MSG_LENGTH + 1]; + + /* QR_set_command() dups this string so doesn't need static */ + char cmdbuffer[ERROR_MSG_LENGTH + 1]; + + mylog("send_query(): conn=%u, query='%s'\n", self, query); + qlog("conn=%u, query='%s'\n", self, query); + + if (!self->sock) + { + CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send Query(connection dead)"); + CC_on_abort(self, NO_TRANS); + return NULL; + } + + /* Indicate that we are sending a query to the backend */ + maxlen = CC_get_max_query_len(self); + if (maxlen > 0 && maxlen < (int) strlen(query) + 1) + { + CC_set_error(self, CONNECTION_MSG_TOO_LONG, "Query string is too long"); + return NULL; + } + + if ((NULL == query) || (query[0] == '\0')) + return NULL; + + if (SOCK_get_errcode(sock) != 0) + { + CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send Query to backend"); + CC_on_abort(self, NO_TRANS | CONN_DEAD); + return NULL; + } + +#define return DONT_CALL_RETURN_FROM_HERE??? + ENTER_INNER_CONN_CS(self, func_cs_count); + SOCK_put_char(sock, 'Q'); + if (SOCK_get_errcode(sock) != 0) + { + CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send Query to backend"); + CC_on_abort(self, NO_TRANS | CONN_DEAD); + goto cleanup; + } + + if (issue_begin) + SOCK_put_n_char(sock, "BEGIN;", 6); + SOCK_put_string(sock, query); + SOCK_flush_output(sock); + + if (SOCK_get_errcode(sock) != 0) + { + CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send Query to backend"); + CC_on_abort(self, NO_TRANS | CONN_DEAD); + goto cleanup; + } + + mylog("send_query: done sending query\n"); + + empty_reqs = 0; + for (wq = query; isspace((UCHAR) *wq); wq++) + ; + if (*wq == '\0') + empty_reqs = 1; + cmdres = qi ? qi->result_in : NULL; + if (cmdres) + used_passed_result_object = TRUE; + else + { + cmdres = QR_Constructor(); + if (!cmdres) + { + CC_set_error(self, CONNECTION_COULD_NOT_RECEIVE, "Could not create result info in send_query."); goto cleanup; } } - res = cmdres; - while (!ReadyToReturn) + res = cmdres; + while (!ReadyToReturn) + { + /* what type of message is coming now ? */ + id = SOCK_get_char(sock); + + if ((SOCK_get_errcode(sock) != 0) || (id == EOF)) + { + CC_set_error(self, CONNECTION_NO_RESPONSE, "No response from the backend"); + + mylog("send_query: 'id' - %s\n", CC_get_errormsg(self)); + CC_on_abort(self, NO_TRANS | CONN_DEAD); + ReadyToReturn = TRUE; + retres = NULL; + break; + } + + mylog("send_query: got id = '%c'\n", id); + + switch (id) + { + case 'A': /* Asynchronous Messages are ignored */ + (void) SOCK_get_int(sock, 4); /* id of notification */ + SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); + /* name of the relation the message comes from */ + break; + case 'C': /* portal query command, no tuples + * returned */ + /* read in the return message from the backend */ + SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); + if (SOCK_get_errcode(sock) != 0) + { + CC_set_error(self, CONNECTION_NO_RESPONSE, "No response from backend while receiving a portal query command"); + mylog("send_query: 'C' - %s\n", CC_get_errormsg(self)); + CC_on_abort(self, NO_TRANS | CONN_DEAD); + ReadyToReturn = TRUE; + retres = NULL; + } + else + { + mylog("send_query: ok - 'C' - %s\n", cmdbuffer); + + if (query_completed) /* allow for "show" style notices */ + { + res->next = QR_Constructor(); + res = res->next; + } + + mylog("send_query: setting cmdbuffer = '%s'\n", cmdbuffer); + + if (strnicmp(cmdbuffer, "BEGIN", 5) == 0) + { + CC_set_in_trans(self); + if (issue_begin) + { + issue_begin = FALSE; + continue; + } + } + else if (strnicmp(cmdbuffer, "COMMIT", 6) == 0) + CC_on_commit(self); + else if (strnicmp(cmdbuffer, "ROLLBACK", 8) == 0) + CC_on_abort(self, NO_TRANS); + else if (strnicmp(cmdbuffer, "END", 3) == 0) + CC_on_commit(self); + else if (strnicmp(cmdbuffer, "ABORT", 5) == 0) + CC_on_abort(self, NO_TRANS); + else + { + trim(cmdbuffer); /* get rid of trailing space */ + ptr = strrchr(cmdbuffer, ' '); + if (ptr) + res->recent_processed_row_count = atoi(ptr + 1); + else + res->recent_processed_row_count = -1; + } + + if (QR_command_successful(res)) + QR_set_status(res, PGRES_COMMAND_OK); + QR_set_command(res, cmdbuffer); + query_completed = TRUE; + mylog("send_query: returning res = %u\n", res); + if (!before_64) + break; + + /* + * (Quotation from the original comments) since + * backend may produce more than one result for some + * commands we need to poll until clear so we send an + * empty query, and keep reading out of the pipe until + * an 'I' is received + */ + + if (empty_reqs == 0) + { + SOCK_put_string(sock, "Q "); + SOCK_flush_output(sock); + empty_reqs++; + } + } + break; + case 'Z': /* Backend is ready for new query (6.4) */ + if (empty_reqs == 0) + { + ReadyToReturn = TRUE; + if (aborted || query_completed) + retres = cmdres; + else + ReadyToReturn = FALSE; + } + break; + case 'N': /* NOTICE: */ + msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); + if (QR_command_successful(res)) + QR_set_status(res, PGRES_NONFATAL_ERROR); + QR_set_notice(res, cmdbuffer); /* will dup this string */ + mylog("~~~ NOTICE: '%s'\n", cmdbuffer); + qlog("NOTICE from backend during send_query: '%s'\n", cmdbuffer); + while (msg_truncated) + msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); + + continue; /* dont return a result -- continue + * reading */ + + case 'I': /* The server sends an empty query */ + /* There is a closing '\0' following the 'I', so we eat it */ + swallow = SOCK_get_char(sock); + if ((swallow != '\0') || SOCK_get_errcode(sock) != 0) + { + CC_set_errornumber(self, CONNECTION_BACKEND_CRAZY); + QR_set_message(res, "Unexpected protocol character from backend (send_query - I)"); + QR_set_status(res, PGRES_FATAL_ERROR); + ReadyToReturn = TRUE; + retres = cmdres; + break; + } + else + { + /* We return the empty query */ + QR_set_status(res, PGRES_EMPTY_QUERY); + } + if (empty_reqs > 0) + { + if (--empty_reqs == 0) + query_completed = TRUE; + } + break; + case 'E': + msg_truncated = SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); + + /* Remove a newline */ + if (msgbuffer[0] != '\0' && msgbuffer[strlen(msgbuffer) - 1] == '\n') + msgbuffer[strlen(msgbuffer) - 1] = '\0'; + + + mylog("send_query: 'E' - %s\n", msgbuffer); + qlog("ERROR from backend during send_query: '%s'\n", msgbuffer); + + /* We should report that an error occured. Zoltan */ + abort_opt = 0; + if (!strncmp(msgbuffer, "FATAL", 5)) + { + CC_set_errornumber(self, CONNECTION_SERVER_REPORTED_ERROR); + abort_opt = NO_TRANS | CONN_DEAD; + } + else + { + CC_set_errornumber(self, CONNECTION_SERVER_REPORTED_WARNING); + if (CC_is_in_trans(self)) + CC_set_in_error_trans(self); + } + CC_on_abort(self, abort_opt); + QR_set_status(res, PGRES_FATAL_ERROR); + QR_set_message(res, msgbuffer); + QR_set_aborted(res, TRUE); + aborted = TRUE; + while (msg_truncated) + msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); + + query_completed = TRUE; + break; + + case 'P': /* get the Portal name */ + SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); + break; + case 'T': /* Tuple results start here */ + if (query_completed) + { + res->next = QR_Constructor(); + if (!res->next) + { + CC_set_error(self, CONNECTION_COULD_NOT_RECEIVE, "Could not create result info in send_query."); + ReadyToReturn = TRUE; + retres = NULL; + break; + } + if (create_keyset) + QR_set_haskeyset(res->next); + mylog("send_query: 'T' no result_in: res = %u\n", res->next); + res = res->next; + + if (qi) + QR_set_cache_size(res, qi->row_size); + } + if (!used_passed_result_object) + { + if (create_keyset) + QR_set_haskeyset(res); + if (!QR_fetch_tuples(res, self, qi ? qi->cursor : NULL)) + { + CC_set_error(self, CONNECTION_COULD_NOT_RECEIVE, QR_get_message(res)); + ReadyToReturn = TRUE; + if (PGRES_FATAL_ERROR == QR_get_status(res)) + retres = cmdres; + else + retres = NULL; + break; + } + query_completed = TRUE; + } + else + { /* next fetch, so reuse an existing result */ + + /* + * called from QR_next_tuple and must return + * immediately. + */ + ReadyToReturn = TRUE; + if (!QR_fetch_tuples(res, NULL, NULL)) + { + CC_set_error(self, CONNECTION_COULD_NOT_RECEIVE, QR_get_message(res)); + retres = NULL; + break; + } + retres = cmdres; + } + break; + case 'D': /* Copy in command began successfully */ + if (query_completed) + { + res->next = QR_Constructor(); + res = res->next; + } + QR_set_status(res, PGRES_COPY_IN); + ReadyToReturn = TRUE; + retres = cmdres; + break; + case 'B': /* Copy out command began successfully */ + if (query_completed) + { + res->next = QR_Constructor(); + res = res->next; + } + QR_set_status(res, PGRES_COPY_OUT); + ReadyToReturn = TRUE; + retres = cmdres; + break; + default: + CC_set_error(self, CONNECTION_BACKEND_CRAZY, "Unexpected protocol character from backend (send_query)"); + CC_on_abort(self, NO_TRANS | CONN_DEAD); + + mylog("send_query: error - %s\n", CC_get_errormsg(self)); + ReadyToReturn = TRUE; + retres = NULL; + break; + } + + /* + * There were no ReadyForQuery response before 6.4. + */ + if (before_64) + { + if (empty_reqs == 0 && query_completed) + break; + } + } + +cleanup: + CLEANUP_FUNC_CONN_CS(func_cs_count, self); + /* + * Break before being ready to return. + */ + if (!ReadyToReturn) + retres = cmdres; + + /* + * Cleanup garbage results before returning. + */ + if (cmdres && retres != cmdres && !used_passed_result_object) + QR_Destructor(cmdres); + /* + * Cleanup the aborted result if specified + */ + if (retres) + { + if (aborted) + { + if (clear_result_on_abort) + { + if (!used_passed_result_object) + { + QR_Destructor(retres); + retres = NULL; + } + } + if (retres) + { + /* + * discard results other than errors. + */ + QResultClass *qres; + for (qres = retres; qres->next; qres = retres) + { + if (QR_get_aborted(qres)) + break; + retres = qres->next; + qres->next = NULL; + QR_Destructor(qres); + } + /* + * If error message isn't set + */ + if (retres && (!CC_get_errormsg(self) || !CC_get_errormsg(self)[0])) + CC_set_errormsg(self, QR_get_message(retres)); + } + } + } +#undef return + return retres; +} + + +int +CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *args, int nargs) +{ + char id, + c, + done; + SocketClass *sock = self->sock; + + /* ERROR_MSG_LENGTH is sufficient */ + char msgbuffer[ERROR_MSG_LENGTH + 1]; + int i; + + mylog("send_function(): conn=%u, fnid=%d, result_is_int=%d, nargs=%d\n", self, fnid, result_is_int, nargs); + + if (!self->sock) + { + CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send function(connection dead)"); + CC_on_abort(self, NO_TRANS); + return FALSE; + } + + if (SOCK_get_errcode(sock) != 0) + { + CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send function to backend"); + CC_on_abort(self, NO_TRANS | CONN_DEAD); + return FALSE; + } + + SOCK_put_string(sock, "F "); + if (SOCK_get_errcode(sock) != 0) + { + CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send function to backend"); + CC_on_abort(self, NO_TRANS | CONN_DEAD); + return FALSE; + } + + SOCK_put_int(sock, fnid, 4); + SOCK_put_int(sock, nargs, 4); + + + mylog("send_function: done sending function\n"); + + for (i = 0; i < nargs; ++i) + { + mylog(" arg[%d]: len = %d, isint = %d, integer = %d, ptr = %u\n", i, args[i].len, args[i].isint, args[i].u.integer, args[i].u.ptr); + + SOCK_put_int(sock, args[i].len, 4); + if (args[i].isint) + SOCK_put_int(sock, args[i].u.integer, 4); + else + SOCK_put_n_char(sock, (char *) args[i].u.ptr, args[i].len); + + + } + + mylog(" done sending args\n"); + + SOCK_flush_output(sock); + mylog(" after flush output\n"); + + done = FALSE; + while (!done) + { + id = SOCK_get_char(sock); + mylog(" got id = %c\n", id); + + switch (id) + { + case 'V': + done = TRUE; + break; /* ok */ + + case 'N': + SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); + mylog("send_function(V): 'N' - %s\n", msgbuffer); + /* continue reading */ + break; + + case 'E': + SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); + CC_set_errormsg(self, msgbuffer); + if (CC_is_in_trans(self)) + CC_set_in_error_trans(self); + CC_on_abort(self, 0); + + mylog("send_function(V): 'E' - %s\n", CC_get_errormsg(self)); + qlog("ERROR from backend during send_function: '%s'\n", CC_get_errormsg(self)); + + return FALSE; + + case 'Z': + break; + + default: + CC_set_error(self, CONNECTION_BACKEND_CRAZY, "Unexpected protocol character from backend (send_function, args)"); + CC_on_abort(self, NO_TRANS | CONN_DEAD); + + mylog("send_function: error - %s\n", CC_get_errormsg(self)); + return FALSE; + } + } + + id = SOCK_get_char(sock); + for (;;) + { + switch (id) + { + case 'G': /* function returned properly */ + mylog(" got G!\n"); + + *actual_result_len = SOCK_get_int(sock, 4); + mylog(" actual_result_len = %d\n", *actual_result_len); + + if (result_is_int) + *((int *) result_buf) = SOCK_get_int(sock, 4); + else + SOCK_get_n_char(sock, (char *) result_buf, *actual_result_len); + + mylog(" after get result\n"); + + c = SOCK_get_char(sock); /* get the last '0' */ + + mylog(" after get 0\n"); + + return TRUE; + + case 'E': + SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); + CC_set_errormsg(self, msgbuffer); + CC_on_abort(self, 0); + mylog("send_function(G): 'E' - %s\n", CC_get_errormsg(self)); + qlog("ERROR from backend during send_function: '%s'\n", CC_get_errormsg(self)); + + return FALSE; + + case 'N': + SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); + + mylog("send_function(G): 'N' - %s\n", msgbuffer); + qlog("NOTICE from backend during send_function: '%s'\n", msgbuffer); + + continue; /* dont return a result -- continue + * reading */ + + case '0': /* empty result */ + return TRUE; + + default: + CC_set_error(self, CONNECTION_BACKEND_CRAZY, "Unexpected protocol character from backend (send_function, result)"); + CC_on_abort(self, NO_TRANS | CONN_DEAD); + + mylog("send_function: error - %s\n", CC_get_errormsg(self)); + return FALSE; + } + } +} + +void +CC_log_error(const char *func, const char *desc, const ConnectionClass *self) +{ +#ifdef PRN_NULLCHECK +#define nullcheck(a) (a ? a : "(NULL)") +#endif + + if (self) + { + qlog("CONN ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->__error_number, nullcheck(self->__error_message)); + mylog("CONN ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->__error_number, nullcheck(self->__error_message)); + qlog(" ------------------------------------------------------------\n"); + qlog(" henv=%u, conn=%u, status=%u, num_stmts=%d\n", self->henv, self, self->status, self->num_stmts); + qlog(" sock=%u, stmts=%u, lobj_type=%d\n", self->sock, self->stmts, self->lobj_type); + + qlog(" ---------------- Socket Info -------------------------------\n"); + if (self->sock) + { + SocketClass *sock = self->sock; + + qlog(" socket=%d, reverse=%d, errornumber=%d, errormsg='%s'\n", sock->socket, sock->reverse, sock->errornumber, nullcheck(sock->errormsg)); + qlog(" buffer_in=%u, buffer_out=%u\n", sock->buffer_in, sock->buffer_out); + qlog(" buffer_filled_in=%d, buffer_filled_out=%d, buffer_read_in=%d\n", sock->buffer_filled_in, sock->buffer_filled_out, sock->buffer_read_in); + } + } + else +{ + qlog("INVALID CONNECTION HANDLE ERROR: func=%s, desc='%s'\n", func, desc); + mylog("INVALID CONNECTION HANDLE ERROR: func=%s, desc='%s'\n", func, desc); +} +#undef PRN_NULLCHECK +} + + +int +CC_send_cancel_request(const ConnectionClass *conn) +{ + int save_errno = SOCK_ERRNO; + int tmpsock = -1; + struct + { + uint32 packetlen; + CancelRequestPacket cp; + } crp; + BOOL ret = TRUE; + + /* Check we have an open connection */ + if (!conn || !conn->sock) + return FALSE; + + /* + * We need to open a temporary connection to the postmaster. Use the + * information saved by connectDB to do this with only kernel calls. + */ + if ((tmpsock = socket(conn->sock->sadr->sa_family, SOCK_STREAM, 0)) < 0) + { + return FALSE; + } + if (connect(tmpsock, conn->sock->sadr, conn->sock->sadr_len) < 0) + { + closesocket(tmpsock); + return FALSE; + } + + /* + * We needn't set nonblocking I/O or NODELAY options here. + */ + crp.packetlen = htonl((uint32) sizeof(crp)); + crp.cp.cancelRequestCode = (MsgType) htonl(CANCEL_REQUEST_CODE); + crp.cp.backendPID = htonl(conn->be_pid); + crp.cp.cancelAuthCode = htonl(conn->be_key); + + if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp)) + { + save_errno = SOCK_ERRNO; + ret = FALSE; + } + + /* Sent it, done */ + closesocket(tmpsock); + SOCK_ERRNO_SET(save_errno); + + return ret; +} + +#else +/* + * Connection class implementation using libpq. + * Memory Allocation for PGconn is handled by libpq. + */ +ConnectionClass * +CC_Constructor() +{ + ConnectionClass *rv; + + rv = (ConnectionClass *) malloc(sizeof(ConnectionClass)); + + if (rv != NULL) { - /* what type of message is coming now ? */ - id = SOCK_get_char(sock); + rv->henv = NULL; /* not yet associated with an environment */ - if ((SOCK_get_errcode(sock) != 0) || (id == EOF)) - { - CC_set_error(self, CONNECTION_NO_RESPONSE, "No response from the backend"); + rv->__error_message = NULL; + rv->__error_number = 0; + rv->errormsg_created = FALSE; - mylog("send_query: 'id' - %s\n", CC_get_errormsg(self)); - CC_on_abort(self, NO_TRANS | CONN_DEAD); - ReadyToReturn = TRUE; - retres = NULL; - break; - } + rv->status = CONN_NOT_CONNECTED; + rv->transact_status = CONN_IN_AUTOCOMMIT; /* autocommit by default */ - mylog("send_query: got id = '%c'\n", id); + CC_conninfo_init(&(rv->connInfo)); + rv->stmts = (StatementClass **) malloc(sizeof(StatementClass *) * STMT_INCREMENT); + if (!rv->stmts) + return NULL; + memset(rv->stmts, 0, sizeof(StatementClass *) * STMT_INCREMENT); - switch (id) - { - case 'A': /* Asynchronous Messages are ignored */ - (void) SOCK_get_int(sock, 4); /* id of notification */ - SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); - /* name of the relation the message comes from */ - break; - case 'C': /* portal query command, no tuples - * returned */ - /* read in the return message from the backend */ - SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); - if (SOCK_get_errcode(sock) != 0) - { - CC_set_error(self, CONNECTION_NO_RESPONSE, "No response from backend while receiving a portal query command"); - mylog("send_query: 'C' - %s\n", CC_get_errormsg(self)); - CC_on_abort(self, NO_TRANS | CONN_DEAD); - ReadyToReturn = TRUE; - retres = NULL; - } - else - { - mylog("send_query: ok - 'C' - %s\n", cmdbuffer); + rv->num_stmts = STMT_INCREMENT; + rv->descs = (DescriptorClass **) malloc(sizeof(DescriptorClass *) * STMT_INCREMENT); + if (!rv->descs) + return NULL; + memset(rv->descs, 0, sizeof(DescriptorClass *) * STMT_INCREMENT); - if (query_completed) /* allow for "show" style notices */ - { - res->next = QR_Constructor(); - res = res->next; - } + rv->num_descs = STMT_INCREMENT; - mylog("send_query: setting cmdbuffer = '%s'\n", cmdbuffer); + rv->lobj_type = PG_TYPE_LO_UNDEFINED; - if (strnicmp(cmdbuffer, "BEGIN", 5) == 0) - { - CC_set_in_trans(self); - if (issue_begin) - { - issue_begin = FALSE; - continue; - } - } - else if (strnicmp(cmdbuffer, "COMMIT", 6) == 0) - CC_on_commit(self); - else if (strnicmp(cmdbuffer, "ROLLBACK", 8) == 0) - CC_on_abort(self, NO_TRANS); - else if (strnicmp(cmdbuffer, "END", 3) == 0) - CC_on_commit(self); - else if (strnicmp(cmdbuffer, "ABORT", 5) == 0) - CC_on_abort(self, NO_TRANS); - else - { - trim(cmdbuffer); /* get rid of trailing space */ - ptr = strrchr(cmdbuffer, ' '); - if (ptr) - res->recent_processed_row_count = atoi(ptr + 1); - else - res->recent_processed_row_count = -1; - } + rv->ntables = 0; + rv->col_info = NULL; - if (QR_command_successful(res)) - QR_set_status(res, PGRES_COMMAND_OK); - QR_set_command(res, cmdbuffer); - query_completed = TRUE; - mylog("send_query: returning res = %u\n", res); - if (!before_64) - break; + rv->translation_option = 0; + rv->translation_handle = NULL; + rv->DataSourceToDriver = NULL; + rv->DriverToDataSource = NULL; + rv->driver_version = ODBCVER; + memset(rv->pg_version, 0, sizeof(rv->pg_version)); + rv->pg_version_number = .0; + rv->pg_version_major = 0; + rv->pg_version_minor = 0; + rv->ms_jet = 0; + rv->unicode = 0; + rv->result_uncommitted = 0; + rv->schema_support = 0; + rv->isolation = SQL_TXN_READ_COMMITTED; + rv->client_encoding = NULL; + rv->server_encoding = NULL; + rv->current_schema = NULL; + rv->num_discardp = 0; + rv->discardp = NULL; - /* - * (Quotation from the original comments) since - * backend may produce more than one result for some - * commands we need to poll until clear so we send an - * empty query, and keep reading out of the pipe until - * an 'I' is received - */ + /* Initialize statement options to defaults */ + /* Statements under this conn will inherit these options */ - if (empty_reqs == 0) - { - SOCK_put_string(sock, "Q "); - SOCK_flush_output(sock); - empty_reqs++; - } - } - break; - case 'Z': /* Backend is ready for new query (6.4) */ - if (empty_reqs == 0) - { - ReadyToReturn = TRUE; - if (aborted || query_completed) - retres = cmdres; - else - ReadyToReturn = FALSE; - } - break; - case 'N': /* NOTICE: */ - msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); - if (QR_command_successful(res)) - QR_set_status(res, PGRES_NONFATAL_ERROR); - QR_set_notice(res, cmdbuffer); /* will dup this string */ - mylog("~~~ NOTICE: '%s'\n", cmdbuffer); - qlog("NOTICE from backend during send_query: '%s'\n", cmdbuffer); - while (msg_truncated) - msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); + InitializeStatementOptions(&rv->stmtOptions); + InitializeARDFields(&rv->ardOptions); + InitializeAPDFields(&rv->apdOptions); + INIT_CONN_CS(rv); + } + return rv; +} - continue; /* dont return a result -- continue - * reading */ - case 'I': /* The server sends an empty query */ - /* There is a closing '\0' following the 'I', so we eat it */ - swallow = SOCK_get_char(sock); - if ((swallow != '\0') || SOCK_get_errcode(sock) != 0) - { - CC_set_errornumber(self, CONNECTION_BACKEND_CRAZY); - QR_set_message(res, "Unexpected protocol character from backend (send_query - I)"); - QR_set_status(res, PGRES_FATAL_ERROR); - ReadyToReturn = TRUE; - retres = cmdres; - break; - } - else - { - /* We return the empty query */ - QR_set_status(res, PGRES_EMPTY_QUERY); - } - if (empty_reqs > 0) - { - if (--empty_reqs == 0) - query_completed = TRUE; - } - break; - case 'E': - msg_truncated = SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); +char +CC_Destructor(ConnectionClass *self) +{ + mylog("enter CC_Destructor, self=%u\n", self); - /* Remove a newline */ - if (msgbuffer[0] != '\0' && msgbuffer[strlen(msgbuffer) - 1] == '\n') - msgbuffer[strlen(msgbuffer) - 1] = '\0'; + if (self->status == CONN_EXECUTING) + return 0; + CC_cleanup(self); /* cleanup libpq connection class and statements */ - mylog("send_query: 'E' - %s\n", msgbuffer); - qlog("ERROR from backend during send_query: '%s'\n", msgbuffer); + mylog("after CC_Cleanup\n"); - /* We should report that an error occured. Zoltan */ - abort_opt = 0; - if (!strncmp(msgbuffer, "FATAL", 5)) - { - CC_set_errornumber(self, CONNECTION_SERVER_REPORTED_ERROR); - abort_opt = NO_TRANS | CONN_DEAD; - } - else - { - CC_set_errornumber(self, CONNECTION_SERVER_REPORTED_WARNING); - if (CC_is_in_trans(self)) - CC_set_in_error_trans(self); - } - CC_on_abort(self, abort_opt); - QR_set_status(res, PGRES_FATAL_ERROR); - QR_set_message(res, msgbuffer); - QR_set_aborted(res, TRUE); - aborted = TRUE; - while (msg_truncated) - msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); + /* Free up statement holders */ + if (self->stmts) + { + free(self->stmts); + self->stmts = NULL; + } + + if (self->descs) + { + free(self->descs); + self->descs = NULL; + } + + mylog("after free statement holders\n"); + + if (self->__error_message) + free(self->__error_message); + DELETE_CONN_CS(self); + free(self); - query_completed = TRUE; - break; + mylog("exit CC_Destructor\n"); - case 'P': /* get the Portal name */ - SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); - break; - case 'T': /* Tuple results start here */ - if (query_completed) - { - res->next = QR_Constructor(); - if (!res->next) - { - CC_set_error(self, CONNECTION_COULD_NOT_RECEIVE, "Could not create result info in send_query."); - ReadyToReturn = TRUE; - retres = NULL; - break; - } - if (create_keyset) - QR_set_haskeyset(res->next); - mylog("send_query: 'T' no result_in: res = %u\n", res->next); - res = res->next; + return 1; +} - if (qi) - QR_set_cache_size(res, qi->row_size); - } - if (!used_passed_result_object) - { - if (create_keyset) - QR_set_haskeyset(res); - if (!QR_fetch_tuples(res, self, qi ? qi->cursor : NULL)) - { - CC_set_error(self, CONNECTION_COULD_NOT_RECEIVE, QR_get_message(res)); - ReadyToReturn = TRUE; - if (PGRES_FATAL_ERROR == QR_get_status(res)) - retres = cmdres; - else - retres = NULL; - break; - } - query_completed = TRUE; - } - else - { /* next fetch, so reuse an existing result */ +/* This is called by SQLDisconnect also */ +char +CC_cleanup(ConnectionClass *self) +{ + int i; + StatementClass *stmt; + DescriptorClass *desc; - /* - * called from QR_next_tuple and must return - * immediately. - */ - ReadyToReturn = TRUE; - if (!QR_fetch_tuples(res, NULL, NULL)) - { - CC_set_error(self, CONNECTION_COULD_NOT_RECEIVE, QR_get_message(res)); - retres = NULL; - break; - } - retres = cmdres; - } - break; - case 'D': /* Copy in command began successfully */ - if (query_completed) - { - res->next = QR_Constructor(); - res = res->next; - } - QR_set_status(res, PGRES_COPY_IN); - ReadyToReturn = TRUE; - retres = cmdres; - break; - case 'B': /* Copy out command began successfully */ - if (query_completed) - { - res->next = QR_Constructor(); - res = res->next; - } - QR_set_status(res, PGRES_COPY_OUT); - ReadyToReturn = TRUE; - retres = cmdres; - break; - default: - CC_set_error(self, CONNECTION_BACKEND_CRAZY, "Unexpected protocol character from backend (send_query)"); - CC_on_abort(self, NO_TRANS | CONN_DEAD); + if (self->status == CONN_EXECUTING) + return FALSE; - mylog("send_query: error - %s\n", CC_get_errormsg(self)); - ReadyToReturn = TRUE; - retres = NULL; - break; - } + mylog("in CC_Cleanup, self=%u\n", self); - /* - * There were no ReadyForQuery response before 6.4. - */ - if (before_64) - { - if (empty_reqs == 0 && query_completed) - break; - } + /* Cancel an ongoing transaction */ + /* We are always in the middle of a transaction, */ + /* even if we are in auto commit. */ + if (self->pgconn) + { + CC_abort(self); + + mylog("after CC_abort\n"); + + /* This closes the connection to the database */ + LIBPQ_Destructor(self->pgconn); + self->pgconn = NULL; } -cleanup: - CLEANUP_FUNC_CONN_CS(func_cs_count, self); - /* - * Break before being ready to return. - */ - if (!ReadyToReturn) - retres = cmdres; + mylog("after LIBPQ destructor\n"); - /* - * Cleanup garbage results before returning. - */ - if (cmdres && retres != cmdres && !used_passed_result_object) - QR_Destructor(cmdres); - /* - * Cleanup the aborted result if specified - */ - if (retres) + /* Free all the stmts on this connection */ + for (i = 0; i < self->num_stmts; i++) { - if (aborted) + stmt = self->stmts[i]; + if (stmt) { - if (clear_result_on_abort) - { - if (!used_passed_result_object) - { - QR_Destructor(retres); - retres = NULL; - } - } - if (retres) - { - /* - * discard results other than errors. - */ - QResultClass *qres; - for (qres = retres; qres->next; qres = retres) - { - if (QR_get_aborted(qres)) - break; - retres = qres->next; - qres->next = NULL; - QR_Destructor(qres); - } - /* - * If error message isn't set - */ - if (retres && (!CC_get_errormsg(self) || !CC_get_errormsg(self)[0])) - CC_set_errormsg(self, QR_get_message(retres)); - } - } - } -#undef return - return retres; -} - + stmt->hdbc = NULL; /* prevent any more dbase interactions */ -int -CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *args, int nargs) -{ - char id, - c, - done; - SocketClass *sock = self->sock; + SC_Destructor(stmt); - /* ERROR_MSG_LENGTH is sufficient */ - char msgbuffer[ERROR_MSG_LENGTH + 1]; - int i; + self->stmts[i] = NULL; + } + } - mylog("send_function(): conn=%u, fnid=%d, result_is_int=%d, nargs=%d\n", self, fnid, result_is_int, nargs); + /* Free all the descs on this connection */ + for (i = 0; i < self->num_descs; i++) + { + desc = self->descs[i]; + if (desc) + { + DC_get_conn(desc) = NULL; /* prevent any more dbase interactions */ + DC_Destructor(desc); + free(desc); + self->descs[i] = NULL; + } + } - if (!self->sock) + /* Check for translation dll */ +#ifdef WIN32 + if (self->translation_handle) { - CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send function(connection dead)"); - CC_on_abort(self, NO_TRANS); - return FALSE; + FreeLibrary(self->translation_handle); + self->translation_handle = NULL; } +#endif - if (SOCK_get_errcode(sock) != 0) + self->status = CONN_NOT_CONNECTED; + self->transact_status = CONN_IN_AUTOCOMMIT; + CC_conninfo_init(&(self->connInfo)); + if (self->client_encoding) { - CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send function to backend"); - CC_on_abort(self, NO_TRANS | CONN_DEAD); - return FALSE; + free(self->client_encoding); + self->client_encoding = NULL; + } + if (self->server_encoding) + { + free(self->server_encoding); + self->server_encoding = NULL; + } + if (self->current_schema) + { + free(self->current_schema); + self->current_schema = NULL; } + /* Free cached table info */ + if (self->col_info) + { + for (i = 0; i < self->ntables; i++) + { + if (self->col_info[i]->result) /* Free the SQLColumns result structure */ + QR_Destructor(self->col_info[i]->result); - SOCK_put_string(sock, "F "); - if (SOCK_get_errcode(sock) != 0) + if (self->col_info[i]->schema) + free(self->col_info[i]->schema); + free(self->col_info[i]); + } + free(self->col_info); + self->col_info = NULL; + } + self->ntables = 0; + if (self->num_discardp > 0 && self->discardp) { - CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send function to backend"); - CC_on_abort(self, NO_TRANS | CONN_DEAD); - return FALSE; + for (i = 0; i < self->num_discardp; i++) + free(self->discardp[i]); + self->num_discardp = 0; + } + if (self->discardp) + { + free(self->discardp); + self->discardp = NULL; } - SOCK_put_int(sock, fnid, 4); - SOCK_put_int(sock, nargs, 4); - - - mylog("send_function: done sending function\n"); + mylog("exit CC_Cleanup\n"); + return TRUE; +} - for (i = 0; i < nargs; ++i) - { - mylog(" arg[%d]: len = %d, isint = %d, integer = %d, ptr = %u\n", i, args[i].len, args[i].isint, args[i].u.integer, args[i].u.ptr); - SOCK_put_int(sock, args[i].len, 4); - if (args[i].isint) - SOCK_put_int(sock, args[i].u.integer, 4); - else - SOCK_put_n_char(sock, (char *) args[i].u.ptr, args[i].len); +static void +exit_nicely(PGconn *conn) +{ + PQfinish(conn); + exit(1); +} +char +CC_connect(ConnectionClass *self, char password_req, char *salt_para) +{ + /* ignore salt_para for now */ + /* QResultClass *res; */ + PGconn *pgconn; + ConnInfo *ci = &(self->connInfo); + int areq = -1,connect_return; + char *encoding; + /* char *conninfo; */ + CSTR func = "CC_connect"; - } + mylog("%s: entering...\n", func); - mylog(" done sending args\n"); + if (password_req != AUTH_REQ_OK) - SOCK_flush_output(sock); - mylog(" after flush output\n"); + /* already connected, just authenticate */ + pgconn = self->pgconn; - done = FALSE; - while (!done) + else { - id = SOCK_get_char(sock); - mylog(" got id = %c\n", id); + qlog("Global Options: Version='%s', fetch=%d, socket=%d, unknown_sizes=%d, max_varchar_size=%d, max_longvarchar_size=%d\n", + POSTGRESDRIVERVERSION, + ci->drivers.fetch_max, + ci->drivers.socket_buffersize, + ci->drivers.unknown_sizes, + ci->drivers.max_varchar_size, + ci->drivers.max_longvarchar_size); + qlog(" disable_optimizer=%d, ksqo=%d, unique_index=%d, use_declarefetch=%d\n", + ci->drivers.disable_optimizer, + ci->drivers.ksqo, + ci->drivers.unique_index, + ci->drivers.use_declarefetch); + qlog(" text_as_longvarchar=%d, unknowns_as_longvarchar=%d, bools_as_char=%d NAMEDATALEN=%d\n", + ci->drivers.text_as_longvarchar, + ci->drivers.unknowns_as_longvarchar, + ci->drivers.bools_as_char, + TABLE_NAME_STORAGE_LEN); - switch (id) + encoding = check_client_encoding(ci->conn_settings); + if (encoding && strcmp(encoding, "OTHER")) + self->client_encoding = strdup(encoding); + else { - case 'V': - done = TRUE; - break; /* ok */ - - case 'N': - SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); - mylog("send_function(V): 'N' - %s\n", msgbuffer); - /* continue reading */ - break; - - case 'E': - SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); - CC_set_errormsg(self, msgbuffer); - if (CC_is_in_trans(self)) - CC_set_in_error_trans(self); - CC_on_abort(self, 0); - - mylog("send_function(V): 'E' - %s\n", CC_get_errormsg(self)); - qlog("ERROR from backend during send_function: '%s'\n", CC_get_errormsg(self)); - - return FALSE; - - case 'Z': - break; - - default: - CC_set_error(self, CONNECTION_BACKEND_CRAZY, "Unexpected protocol character from backend (send_function, args)"); - CC_on_abort(self, NO_TRANS | CONN_DEAD); + encoding = check_client_encoding(ci->drivers.conn_settings); + if (encoding && strcmp(encoding, "OTHER")) + self->client_encoding = strdup(encoding); + } + if (self->client_encoding) + self->ccsc = pg_CS_code(self->client_encoding); + qlog(" extra_systable_prefixes='%s', conn_settings='%s' conn_encoding='%s'\n", + ci->drivers.extra_systable_prefixes, + ci->drivers.conn_settings, + encoding ? encoding : ""); - mylog("send_function: error - %s\n", CC_get_errormsg(self)); - return FALSE; + if (self->status != CONN_NOT_CONNECTED) + { + CC_set_error(self, CONN_OPENDB_ERROR, "Already connected."); + return 0; } - } - id = SOCK_get_char(sock); - for (;;) - { - switch (id) + if (ci->port[0] == '\0' || +#ifdef WIN32 + ci->server[0] == '\0' || +#endif /* WIN32 */ + ci->database[0] == '\0') { - case 'G': /* function returned properly */ - mylog(" got G!\n"); + CC_set_error(self, CONN_INIREAD_ERROR, "Missing server name, port, or database name in call to CC_connect."); + return 0; + } - *actual_result_len = SOCK_get_int(sock, 4); - mylog(" actual_result_len = %d\n", *actual_result_len); + mylog("CC_connect(): DSN = '%s', server = '%s', port = '%s'," + " database = '%s', username = '%s'," + " password='%s'\n", ci->dsn, ci->server, ci->port, + ci->database, ci->username, ci->password ? "xxxxx" : ""); - if (result_is_int) - *((int *) result_buf) = SOCK_get_int(sock, 4); - else - SOCK_get_n_char(sock, (char *) result_buf, *actual_result_len); - mylog(" after get result\n"); + mylog("connecting to the server \n"); - c = SOCK_get_char(sock); /* get the last '0' */ + connect_return = LIBPQ_connect(self); + if(0 == connect_return) + { + CC_set_error(self, CONNECTION_COULD_NOT_ESTABLISH, "Could not connect to the server"); + return 0; + } - mylog(" after get 0\n"); + mylog("connection to the database succeeded.\n"); - return TRUE; + } - case 'E': - SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); - CC_set_errormsg(self, msgbuffer); - CC_on_abort(self, 0); - mylog("send_function(G): 'E' - %s\n", CC_get_errormsg(self)); - qlog("ERROR from backend during send_function: '%s'\n", CC_get_errormsg(self)); + CC_clear_error(self); /* clear any password error */ - return FALSE; + CC_set_translation(self); - case 'N': - SOCK_get_string(sock, msgbuffer, ERROR_MSG_LENGTH); + /* + * Send any initial settings + */ - mylog("send_function(G): 'N' - %s\n", msgbuffer); - qlog("NOTICE from backend during send_function: '%s'\n", msgbuffer); + /* + * Get the version number first so we can check it before sending options + * that are now obsolete. DJP 21/06/2002 + */ - continue; /* dont return a result -- continue - * reading */ + CC_lookup_pg_version(self); /* Get PostgreSQL version for + SQLGetInfo use */ + /* + * Since these functions allocate statements, and since the connection + * is not established yet, it would violate odbc state transition + * rules. Therefore, these functions call the corresponding local + * function instead. + */ + CC_send_settings(self); + CC_clear_error(self); /* clear any error */ + CC_lookup_lo(self); /* a hack to get the oid of + our large object oid type */ - case '0': /* empty result */ - return TRUE; + /* + * Multibyte handling is available ? + */ + if (PG_VERSION_GE(self, 6.4)) + { + CC_lookup_characterset(self); + if (CC_get_errornumber(self) != 0) + return 0; - default: - CC_set_error(self, CONNECTION_BACKEND_CRAZY, "Unexpected protocol character from backend (send_function, result)"); - CC_on_abort(self, NO_TRANS | CONN_DEAD); + if (self->unicode) + { + if (!self->client_encoding || + stricmp(self->client_encoding, "UNICODE")) + { + QResultClass *res; + if (PG_VERSION_LT(self, 7.1)) + { + CC_set_error(self, CONN_NOT_IMPLEMENTED_ERROR, "UTF-8 conversion isn't implemented before 7.1"); + return 0; + } + if (self->client_encoding) + free(self->client_encoding); + self->client_encoding = NULL; + res = LIBPQ_execute_query(self,"set client_encoding to 'UTF8'"); + if (res) + { + self->client_encoding = strdup("UNICODE"); + self->ccsc = pg_CS_code(self->client_encoding); + QR_Destructor(res); - mylog("send_function: error - %s\n", CC_get_errormsg(self)); - return FALSE; + } + } } } -} - - -char -CC_send_settings(ConnectionClass *self) -{ - /* char ini_query[MAX_MESSAGE_LEN]; */ - ConnInfo *ci = &(self->connInfo); - -/* QResultClass *res; */ - HSTMT hstmt; - StatementClass *stmt; - RETCODE result; - char status = TRUE; - char *cs, - *ptr; -#ifdef HAVE_STRTOK_R - char *last; -#endif /* HAVE_STRTOK_R */ - CSTR func = "CC_send_settings"; - - - mylog("%s: entering...\n", func); + else if (self->unicode) + { + CC_set_error(self, CONN_NOT_IMPLEMENTED_ERROR, "Unicode isn't supported before 6.4"); + return 0; + } -/* - * This function must use the local odbc API functions since the odbc state - * has not transitioned to "connected" yet. - */ + ci->updatable_cursors = 0; +#ifdef DRIVER_CURSOR_IMPLEMENT + if (!ci->drivers.use_declarefetch && + PG_VERSION_GE(self, 7.0)) /* Tid scan since 7.0 */ + ci->updatable_cursors = ci->allow_keyset; +#endif /* DRIVER_CURSOR_IMPLEMENT */ - result = PGAPI_AllocStmt(self, &hstmt); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) - return FALSE; - stmt = (StatementClass *) hstmt; + CC_clear_error(self); /* clear any initial command errors */ + self->status = CONN_CONNECTED; - stmt->internal = TRUE; /* ensure no BEGIN/COMMIT/ABORT stuff */ + mylog("%s: returning...\n", func); - /* Set the Datestyle to the format the driver expects it to be in */ - result = PGAPI_ExecDirect(hstmt, "set DateStyle to 'ISO'", SQL_NTS, 0); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) - status = FALSE; + return 1; - mylog("%s: result %d, status %d from set DateStyle\n", func, result, status); +} - /* Disable genetic optimizer based on global flag */ - if (ci->drivers.disable_optimizer) - { - result = PGAPI_ExecDirect(hstmt, "set geqo to 'OFF'", SQL_NTS, 0); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) - status = FALSE; - mylog("%s: result %d, status %d from set geqo\n", func, result, status); +/* + * Create a more informative error message by concatenating the connection + * error message with its libpq error message. + */ +char * +CC_create_errormsg(ConnectionClass *self) +{ + PGconn *pgconn = self->pgconn; + char msg[4096]; - } + mylog("enter CC_create_errormsg\n"); - /* KSQO (not applicable to 7.1+ - DJP 21/06/2002) */ - if (ci->drivers.ksqo && PG_VERSION_LT(self, 7.1)) - { - result = PGAPI_ExecDirect(hstmt, "set ksqo to 'ON'", SQL_NTS, 0); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) - status = FALSE; + msg[0] = '\0'; - mylog("%s: result %d, status %d from set ksqo\n", func, result, status); + if (CC_get_errormsg(self)) + strncpy(msg, CC_get_errormsg(self), sizeof(msg)); - } + mylog("msg = '%s'\n", msg); - /* extra_float_digits (applicable since 7.4) */ - if (PG_VERSION_GT(self, 7.3)) - { - result = PGAPI_ExecDirect(hstmt, "set extra_float_digits to 2", SQL_NTS, 0); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) - status = FALSE; + mylog("exit CC_create_errormsg\n"); + return msg ? strdup(msg) : NULL; +} - mylog("%s: result %d, status %d from set extra_float_digits\n", func, result, status); - } +void CC_on_abort(ConnectionClass *conn, UDWORD opt) +{ + BOOL set_no_trans = FALSE; - /* Global settings */ - if (ci->drivers.conn_settings[0] != '\0') + if (0 != (opt & CONN_DEAD)) + opt |= NO_TRANS; + if (CC_is_in_trans(conn)) { - cs = strdup(ci->drivers.conn_settings); -#ifdef HAVE_STRTOK_R - ptr = strtok_r(cs, ";", &last); -#else - ptr = strtok(cs, ";"); -#endif /* HAVE_STRTOK_R */ - while (ptr) +#ifdef DRIVER_CURSOR_IMPLEMENT + if (conn->result_uncommitted) + ProcessRollback(conn, TRUE); +#endif /* DRIVER_CURSOR_IMPLEMENT */ + if (0 != (opt & NO_TRANS)) { - result = PGAPI_ExecDirect(hstmt, ptr, SQL_NTS, 0); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) - status = FALSE; - - mylog("%s: result %d, status %d from '%s'\n", func, result, status, ptr); - -#ifdef HAVE_STRTOK_R - ptr = strtok_r(NULL, ";", &last); -#else - ptr = strtok(NULL, ";"); -#endif /* HAVE_STRTOK_R */ + CC_set_no_trans(conn); + CC_set_no_manual_trans(conn); + set_no_trans = TRUE; } - - free(cs); } - - /* Per Datasource settings */ - if (ci->conn_settings[0] != '\0') + CC_clear_cursors(conn, TRUE); + if (0 != (opt & CONN_DEAD)) { - cs = strdup(ci->conn_settings); -#ifdef HAVE_STRTOK_R - ptr = strtok_r(cs, ";", &last); -#else - ptr = strtok(cs, ";"); -#endif /* HAVE_STRTOK_R */ - while (ptr) + conn->status = CONN_DOWN; + if (conn->pgconn) { - result = PGAPI_ExecDirect(hstmt, ptr, SQL_NTS, 0); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) - status = FALSE; - - mylog("%s: result %d, status %d from '%s'\n", func, result, status, ptr); - -#ifdef HAVE_STRTOK_R - ptr = strtok_r(NULL, ";", &last); -#else - ptr = strtok(NULL, ";"); -#endif /* HAVE_STRTOK_R */ + LIBPQ_Destructor(conn->pgconn); + conn->pgconn = NULL; } - - free(cs); } - - - PGAPI_FreeStmt(hstmt, SQL_DROP); - - return status; + else if (set_no_trans) + CC_discard_marked_plans(conn); + conn->result_uncommitted = 0; } - /* - * This function is just a hack to get the oid of our Large Object oid type. - * If a real Large Object oid type is made part of Postgres, this function - * will go away and the define 'PG_TYPE_LO' will be updated. + * The "result_in" is only used by QR_next_tuple() to fetch another group of rows into + * the same existing QResultClass (this occurs when the tuple cache is depleted and + * needs to be re-filled). + * + * The "cursor" is used by SQLExecute to associate a statement handle as the cursor name + * (i.e., C3326857) for SQL select statements. This cursor is then used in future + * 'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements. */ -void -CC_lookup_lo(ConnectionClass *self) +QResultClass * +CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag) { - HSTMT hstmt; - StatementClass *stmt; - RETCODE result; - CSTR func = "CC_lookup_lo"; - - mylog("%s: entering...\n", func); + QResultClass *cmdres = NULL, + *retres = NULL, + *res ; + BOOL clear_result_on_abort = ((flag & CLEAR_RESULT_ON_ABORT) != 0), + create_keyset = ((flag & CREATE_KEYSET) != 0), + issue_begin = ((flag & GO_INTO_TRANSACTION) != 0 && !CC_is_in_trans(self)); + char *wq; + int id=0; + PGconn *pgconn = self->pgconn; + int maxlen, + empty_reqs; + BOOL ReadyToReturn = FALSE, + query_completed = FALSE, + before_64 = PG_VERSION_LT(self, 6.4), + aborted = FALSE, + used_passed_result_object = FALSE; + int func_cs_count = 0; -/* - * This function must use the local odbc API functions since the odbc state - * has not transitioned to "connected" yet. - */ - result = PGAPI_AllocStmt(self, &hstmt); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) - return; - stmt = (StatementClass *) hstmt; - result = PGAPI_ExecDirect(hstmt, "select oid from pg_type where typname='" PG_TYPE_LO_NAME "'", SQL_NTS, 0); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) - { - PGAPI_FreeStmt(hstmt, SQL_DROP); - return; - } + mylog("send_query(): conn=%u, query='%s'\n", self, query); + qlog("conn=%u, query='%s'\n", self, query); - result = PGAPI_Fetch(hstmt); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + if (!self->pgconn) { - PGAPI_FreeStmt(hstmt, SQL_DROP); - return; + CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send Query(connection dead)"); + CC_on_abort(self, NO_TRANS); + return NULL; } - - result = PGAPI_GetData(hstmt, 1, SQL_C_SLONG, &self->lobj_type, sizeof(self->lobj_type), NULL); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + /* Indicate that we are sending a query to the backend */ + maxlen = CC_get_max_query_len(self); + if (maxlen > 0 && maxlen < (int) strlen(query) + 1) { - PGAPI_FreeStmt(hstmt, SQL_DROP); - return; + CC_set_error(self, CONNECTION_MSG_TOO_LONG, "Query string is too long"); + return NULL; } - mylog("Got the large object oid: %d\n", self->lobj_type); - qlog(" [ Large Object oid = %d ]\n", self->lobj_type); + if ((NULL == query) || (query[0] == '\0')) + return NULL; - result = PGAPI_FreeStmt(hstmt, SQL_DROP); -} +#define return DONT_CALL_RETURN_FROM_HERE??? + ENTER_INNER_CONN_CS(self, func_cs_count); + if (issue_begin) + res = LIBPQ_execute_query(self,"BEGIN"); + res = LIBPQ_execute_query(self,query); -/* - * This function initializes the version of PostgreSQL from - * connInfo.protocol that we're connected to. - * h-inoue 01-2-2001 - */ -void -CC_initialize_pg_version(ConnectionClass *self) -{ - strcpy(self->pg_version, self->connInfo.protocol); - if (PROTOCOL_62(&self->connInfo)) + if((!res) || (res->status == PGRES_EMPTY_QUERY) ) { - self->pg_version_number = (float) 6.2; - self->pg_version_major = 6; - self->pg_version_minor = 2; + QR_Destructor(res); + goto cleanup; } - else if (PROTOCOL_63(&self->connInfo)) + else { - self->pg_version_number = (float) 6.3; - self->pg_version_major = 6; - self->pg_version_minor = 3; + mylog("send_query: done sending query\n"); + + empty_reqs = 0; + for (wq = query; isspace((UCHAR) *wq); wq++) + ; + if (*wq == '\0') + empty_reqs = 1; + cmdres = qi ? qi->result_in : NULL; + if (cmdres) + used_passed_result_object = TRUE; + if (!used_passed_result_object) + { + if (create_keyset) + QR_set_haskeyset(res->next); + if (!QR_fetch_tuples(res, self, qi ? qi->cursor : NULL)) + { + CC_set_error(self, CONNECTION_COULD_NOT_RECEIVE, QR_get_message(res)); + if (PGRES_FATAL_ERROR == QR_get_status(res)) + retres = cmdres; + else + retres = NULL; + } + } + else + { /* next fetch, so reuse an existing result */ + /* + * called from QR_next_tuple and must return + * immediately. + */ + if (!QR_fetch_tuples(res, NULL, NULL)) + { + CC_set_error(self, CONNECTION_COULD_NOT_RECEIVE, QR_get_message(res)); + retres = NULL; + } + retres = cmdres; + } } - else + +cleanup: + CLEANUP_FUNC_CONN_CS(func_cs_count, self); + /* + * Cleanup garbage results before returning. + */ + if (cmdres && retres != cmdres && !used_passed_result_object) { - self->pg_version_number = (float) 6.4; - self->pg_version_major = 6; - self->pg_version_minor = 4; + QR_Destructor(cmdres); + } + /* + * Cleanup the aborted result if specified + */ + if (retres) + { + if (aborted) + { + if (clear_result_on_abort) + { + if (!used_passed_result_object) + { + QR_Destructor(retres); + retres = NULL; + } + } + if (retres) + { + /* + * discard results other than errors. + */ + QResultClass *qres; + for (qres = retres; qres->next; qres = retres) + { + if (QR_get_aborted(qres)) + break; + retres = qres->next; + qres->next = NULL; + QR_Destructor(qres); + } + /* + * If error message isn't set + */ + if (retres && (!CC_get_errormsg(self) || !CC_get_errormsg(self)[0])) + CC_set_errormsg(self, QR_get_message(retres)); + } + } } +#undef return + return res; } - -/* - * This function gets the version of PostgreSQL that we're connected to. - * This is used to return the correct info in SQLGetInfo - * DJP - 25-1-2001 - */ -void -CC_lookup_pg_version(ConnectionClass *self) +int +CC_send_function(ConnectionClass *self, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *args, int nargs) { - HSTMT hstmt; - StatementClass *stmt; - RETCODE result; - char szVersion[32]; - int major, - minor; - CSTR func = "CC_lookup_pg_version"; + char done; + PGconn *pgconn=self->pgconn; - mylog("%s: entering...\n", func); -/* - * This function must use the local odbc API functions since the odbc state - * has not transitioned to "connected" yet. - */ - result = PGAPI_AllocStmt(self, &hstmt); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) - return; - stmt = (StatementClass *) hstmt; + mylog("send_function(): conn=%u, fnid=%d, result_is_int=%d, nargs=%d\n", self, fnid, result_is_int, nargs); - /* get the server's version if possible */ - result = PGAPI_ExecDirect(hstmt, "select version()", SQL_NTS, 0); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + if (!self->pgconn) { - PGAPI_FreeStmt(hstmt, SQL_DROP); - return; + CC_set_error(self, CONNECTION_COULD_NOT_SEND, "Could not send function(connection dead)"); + CC_on_abort(self, NO_TRANS); + return FALSE; } - result = PGAPI_Fetch(hstmt); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) - { - PGAPI_FreeStmt(hstmt, SQL_DROP); - return; - } + mylog("send_function: done sending function\n"); - result = PGAPI_GetData(hstmt, 1, SQL_C_CHAR, self->pg_version, MAX_INFO_STRING, NULL); - if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) - { - PGAPI_FreeStmt(hstmt, SQL_DROP); - return; - } + /* Need to implement this */ - /* - * Extract the Major and Minor numbers from the string. This assumes - * the string starts 'Postgresql X.X' - */ - strcpy(szVersion, "0.0"); - if (sscanf(self->pg_version, "%*s %d.%d", &major, &minor) >= 2) - { - sprintf(szVersion, "%d.%d", major, minor); - self->pg_version_major = major; - self->pg_version_minor = minor; - } - self->pg_version_number = (float) atof(szVersion); - if (PG_VERSION_GE(self, 7.3)) - self->schema_support = 1; + mylog(" done sending args\n"); - mylog("Got the PostgreSQL version string: '%s'\n", self->pg_version); - mylog("Extracted PostgreSQL version number: '%1.1f'\n", self->pg_version_number); - qlog(" [ PostgreSQL version string = '%s' ]\n", self->pg_version); - qlog(" [ PostgreSQL version number = '%1.1f' ]\n", self->pg_version_number); + mylog(" after flush output\n"); - result = PGAPI_FreeStmt(hstmt, SQL_DROP); + done = FALSE; + return TRUE; } @@ -2176,148 +2994,268 @@ CC_log_error(const char *func, const char *desc, const ConnectionClass *self) mylog("CONN ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->__error_number, nullcheck(self->__error_message)); qlog(" ------------------------------------------------------------\n"); qlog(" henv=%u, conn=%u, status=%u, num_stmts=%d\n", self->henv, self, self->status, self->num_stmts); - qlog(" sock=%u, stmts=%u, lobj_type=%d\n", self->sock, self->stmts, self->lobj_type); - - qlog(" ---------------- Socket Info -------------------------------\n"); - if (self->sock) - { - SocketClass *sock = self->sock; - - qlog(" socket=%d, reverse=%d, errornumber=%d, errormsg='%s'\n", sock->socket, sock->reverse, sock->errornumber, nullcheck(sock->errormsg)); - qlog(" buffer_in=%u, buffer_out=%u\n", sock->buffer_in, sock->buffer_out); - qlog(" buffer_filled_in=%d, buffer_filled_out=%d, buffer_read_in=%d\n", sock->buffer_filled_in, sock->buffer_filled_out, sock->buffer_read_in); - } } else -{ + { qlog("INVALID CONNECTION HANDLE ERROR: func=%s, desc='%s'\n", func, desc); mylog("INVALID CONNECTION HANDLE ERROR: func=%s, desc='%s'\n", func, desc); -} + } #undef PRN_NULLCHECK } + int -CC_get_max_query_len(const ConnectionClass *conn) +CC_send_cancel_request(const ConnectionClass *conn) { - int value; + int ret = 0,errbufsize=256; + char errbuf[256]; + PGcancel *cancel; - /* Long Queries in 7.0+ */ - if (PG_VERSION_GE(conn, 7.0)) - value = 0 /* MAX_STATEMENT_LEN */ ; - /* Prior to 7.0 we used 2*BLCKSZ */ - else if (PG_VERSION_GE(conn, 6.5)) - value = (2 * BLCKSZ); + + cancel = PQgetCancel(conn->pgconn); + if(!cancel) + { + PQfreeCancel(cancel); + return FALSE; + } + ret=PQcancel(cancel, errbuf,errbufsize); + if(1 == ret) + return TRUE; else - /* Prior to 6.5 we used BLCKSZ */ - value = BLCKSZ; - return value; + { + PQfreeCancel(cancel); + return FALSE; + } + return ret; } -/* - * This doesn't really return the CURRENT SCHEMA - * but there's no alternative. - */ -const char * -CC_get_current_schema(ConnectionClass *conn) + +void +LIBPQ_Destructor(PGconn *pgconn) { - if (!conn->current_schema && conn->schema_support) + mylog("entering PGCONN_Destructor \n"); + PQfinish(pgconn); + mylog("exiting PGCONN_Destructor \n"); +} + + +int +LIBPQ_connect(ConnectionClass *self) +{ + char *conninfo; + mylog("connecting to the database using %s as the server\n",self->connInfo.server); + if(self->connInfo.server != '\0') { - QResultClass *res; + conninfo = (char *)malloc((sizeof(char) * strlen(" host=") + strlen(self->connInfo.server) + 1)); + if(!conninfo) + { + CC_set_error(self, CONN_MEMORY_ALLOCATION_FAILED,"Could not allocate memory for connection string(server)"); + mylog("could not allocate memory for server \n"); + } + conninfo = strcpy(conninfo," host="); + conninfo = strcat(conninfo,self->connInfo.server); - if (res = CC_send_query(conn, "select current_schema()", NULL, CLEAR_RESULT_ON_ABORT), res) + } + mylog("the size is %d \n",strlen(conninfo)); + if(self->connInfo.port[0] != '\0') + { + size_t size=(sizeof(char) * (strlen(" port=") + strlen(self->connInfo.port) + 1)); + conninfo = (char *)realloc(conninfo,size+strlen(conninfo)); + if(!conninfo) { - if (QR_get_num_total_tuples(res) == 1) - conn->current_schema = strdup(QR_get_value_backend_row(res, 0, 0)); - QR_Destructor(res); + CC_set_error(self, CONN_MEMORY_ALLOCATION_FAILED,"Could not allocate memory for connection string(port)"); + mylog("could not allocate memory for port \n"); } + conninfo = strcat(conninfo," port="); + conninfo = strcat(conninfo,self->connInfo.port); } - return (const char *) conn->current_schema; -} -int -CC_send_cancel_request(const ConnectionClass *conn) -{ - int save_errno = SOCK_ERRNO; - int tmpsock = -1; - struct + + if(self->connInfo.database[0] != '\0') { - uint32 packetlen; - CancelRequestPacket cp; - } crp; - BOOL ret = TRUE; + size_t size= (sizeof(char) * (strlen(" dbname=") + strlen(self->connInfo.database) + 1)); + conninfo = (char *)realloc(conninfo,size+strlen(conninfo)); + if(!conninfo) + { + CC_set_error(self, CONN_MEMORY_ALLOCATION_FAILED,"Could not allocate memory for connection string(database)"); + mylog("i could not allocate memory for dbname \n"); + } + conninfo = strcat(conninfo," dbname="); + conninfo = strcat(conninfo,self->connInfo.database); + } - /* Check we have an open connection */ - if (!conn || !conn->sock) - return FALSE; - /* - * We need to open a temporary connection to the postmaster. Use the - * information saved by connectDB to do this with only kernel calls. - */ - if ((tmpsock = socket(conn->sock->sadr->sa_family, SOCK_STREAM, 0)) < 0) + if(self->connInfo.username[0] != '\0') { - return FALSE; + size_t size = (sizeof(char) * (strlen(" user=") + strlen(self->connInfo.username) + 1)); + conninfo = (char *)realloc(conninfo,size+strlen(conninfo)); + if(!conninfo) + { + CC_set_error(self, CONN_MEMORY_ALLOCATION_FAILED,"Could not allocate memory for connection string(username)"); + mylog("i could not allocate memory for username \n"); + } + conninfo = strcat(conninfo," user="); + conninfo = strcat(conninfo,self->connInfo.username); } - if (connect(tmpsock, conn->sock->sadr, conn->sock->sadr_len) < 0) + + + if(self->connInfo.password[0] != '\0') { - closesocket(tmpsock); - return FALSE; + size_t size = (sizeof(char) * (strlen(" password=") + strlen(self->connInfo.password) + 1)); + conninfo = (char *)realloc(conninfo,size+strlen(conninfo)); + if(!conninfo) + { + CC_set_error(self, CONN_MEMORY_ALLOCATION_FAILED,"Could not allocate memory for connection string(password)"); + mylog("i could not allocate memory for password \n"); + } + conninfo = strcat(conninfo," password="); + conninfo = strcat(conninfo,self->connInfo.password); } - /* - * We needn't set nonblocking I/O or NODELAY options here. - */ - crp.packetlen = htonl((uint32) sizeof(crp)); - crp.cp.cancelRequestCode = (MsgType) htonl(CANCEL_REQUEST_CODE); - crp.cp.backendPID = htonl(conn->be_pid); - crp.cp.cancelAuthCode = htonl(conn->be_key); - - if (send(tmpsock, (char *) &crp, sizeof(crp), 0) != (int) sizeof(crp)) + self->pgconn = PQconnectdb(conninfo); + if (PQstatus(self->pgconn) != CONNECTION_OK) { - save_errno = SOCK_ERRNO; - ret = FALSE; + CC_set_error(self,CONNECTION_COULD_NOT_ESTABLISH,PQerrorMessage(self->pgconn)); + mylog("could not establish connection to the database %s \n",PQerrorMessage(self->pgconn)); + exit_nicely(self->pgconn); + free(conninfo); + return 0; } + /* free the conninfo structure */ + free(conninfo); + mylog("connection to the database succeeded.\n"); + return 1; +} - /* Sent it, done */ - closesocket(tmpsock); - SOCK_ERRNO_SET(save_errno); - return ret; +QResultClass * +LIBPQ_execute_query(ConnectionClass *self,char *query) +{ + QResultClass *qres; + PGresult *pgres; + char *ptr; + char cmdbuffer[ERROR_MSG_LENGTH + 1]; + strcpy(cmdbuffer,query); + mylog("send_query: setting cmdbuffer = '%s'\n", cmdbuffer); + pgres = PQexec(self->pgconn,query); + qres=QR_Constructor(); + if(!qres) + { + CC_set_error(self, CONNECTION_COULD_NOT_RECEIVE, "Could not allocate memory for result set"); + QR_Destructor(qres); + } + qres->status = PQresultStatus(pgres); + if( (PQresultStatus(pgres) == PGRES_COMMAND_OK) ) + { + mylog("The query was executed successfully and the query did not return any result \n"); + PQclear(pgres); + return qres; + } + if ( (PQresultStatus(pgres) != PGRES_EMPTY_QUERY) && (PQresultStatus(pgres) != PGRES_TUPLES_OK) ) + { + mylog("inside if loop got no result from the empty query. (probably database does not exist)\n"); + CC_set_error(self, CONNECTION_NO_SUCH_DATABASE, "The database does not exist on the server"); + PQclear(pgres); + return qres; + } + if (strnicmp(cmdbuffer, "BEGIN", 5) == 0) + { + CC_set_in_trans(self); + } + else if (strnicmp(cmdbuffer, "COMMIT", 6) == 0) + CC_on_commit(self); + else if (strnicmp(cmdbuffer, "ROLLBACK", 8) == 0) + CC_on_abort(self, NO_TRANS); + else if (strnicmp(cmdbuffer, "END", 3) == 0) + CC_on_commit(self); + else if (strnicmp(cmdbuffer, "ABORT", 5) == 0) + CC_on_abort(self, NO_TRANS); + else + { + trim(cmdbuffer); /* get rid of trailing space */ + ptr = strrchr(cmdbuffer, ' '); + if (ptr) + qres->recent_processed_row_count = atoi(ptr + 1); + else + qres->recent_processed_row_count = -1; + } + qres=CC_mapping(pgres,qres); + QR_set_command(qres, cmdbuffer); + return qres; } -int CC_mark_a_plan_to_discard(ConnectionClass *conn, const char *plan) -{ - int cnt = conn->num_discardp + 1; - char *pname; +/* + * This function populates the manual_tuples of QResultClass using PGresult class. + */ - CC_REALLOC_return_with_error(conn->discardp, char *, - (cnt * sizeof(char *)), conn, "Couldn't alloc discardp.", -1) - CC_MALLOC_return_with_error(pname, char, (strlen(plan) + 1), - conn, "Couldn't alloc discardp mem.", -1) - strcpy(pname, plan); - conn->discardp[conn->num_discardp++] = pname; - return 1; -} -int CC_discard_marked_plans(ConnectionClass *conn) +QResultClass * +CC_mapping(PGresult *pgres,QResultClass *qres) { - int i, cnt; - QResultClass *res; - char cmd[32]; - - if ((cnt = conn->num_discardp) <= 0) - return 0; - for (i = cnt - 1; i >= 0; i--) - { - sprintf(cmd, "DEALLOCATE \"%s\"", conn->discardp[i]); - res = CC_send_query(conn, cmd, NULL, CLEAR_RESULT_ON_ABORT); - if (res) + int i=0,j=0; + TupleNode *node, *temp; + Oid typid; + int atttypmod,typlen; + int num_attributes = PQnfields(pgres); + int num_tuples = PQntuples(pgres); + + CI_set_num_fields(qres->fields, num_attributes); + for(i = 0 ; i < num_attributes ; i++) + { + typid = PQftype(pgres,i); + atttypmod = PQfmod(pgres,i); + typlen = PQfsize(pgres,i); + if(typlen == -1 || PG_TYPE_VARCHAR == typid) + typlen = MAX_VARCHAR_SIZE; + switch (typid) { - QR_Destructor(res); - free(conn->discardp[i]); - conn->num_discardp--; + case PG_TYPE_DATETIME: + case PG_TYPE_TIMESTAMP_NO_TMZONE: + case PG_TYPE_TIME: + case PG_TYPE_TIME_WITH_TMZONE: + break; + default: + atttypmod -= 4; } - else - return -1; + if (atttypmod < 0) + atttypmod = -1; + CI_set_field_info(qres->fields, i, PQfname(pgres,i), + typid, (Int2)typlen, atttypmod); } - return 1; + qres->manual_tuples = TL_Constructor(num_attributes); + qres->manual_tuples->num_tuples = (Int4)num_tuples; + for(i=0;i < num_tuples;i++) + { + node = (TupleNode *)malloc(sizeof(TupleNode) + (num_attributes ) * sizeof(TupleField)); + if(!node) + { + QR_set_status(qres, PGRES_FATAL_ERROR); + QR_set_message(qres, "Error could not allocate memory for row."); + } + if (i==0) + { + qres->manual_tuples->list_start = qres->manual_tuples->list_end = node; + qres->manual_tuples->lastref = node; + qres->manual_tuples->last_indexed = 0; + qres->manual_tuples->list_end->next = NULL; + } + else + { + temp = qres->manual_tuples->list_end; + qres->manual_tuples->list_end->next = node; + qres->manual_tuples->list_end = node; + qres->manual_tuples->list_end->prev = temp; + qres->manual_tuples->list_end->next = NULL; + } + for(j=0;j < num_attributes ;j++) + { + set_tuplefield_string(&qres->manual_tuples->list_end->tuple[j],PQgetvalue(pgres,i,j)); + } + + } + return qres; } + + +#endif /* USE_LIBPQ */ + + diff --git a/connection.h b/connection.h index 279ff79..018e256 100644 --- a/connection.h +++ b/connection.h @@ -6,6 +6,8 @@ * */ +#ifndef USE_LIBPQ + #ifndef __CONNECTION_H__ #define __CONNECTION_H__ @@ -461,3 +463,5 @@ int CC_discard_marked_plans(ConnectionClass *conn); #define CONN_DEAD (1L << 1) /* connection is no longer valid */ #endif /* __CONNECTION_H__ */ + +#endif /* USE_LIBPQ */ diff --git a/convert.c b/convert.c index 58c5449..c560e8b 100644 --- a/convert.c +++ b/convert.c @@ -36,7 +36,13 @@ #include "bind.h" #include "pgtypes.h" #include "lobj.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "pgapifunc.h" #ifdef __CYGWIN__ @@ -470,9 +476,12 @@ copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 std_time.m = tim->tm_mon + 1; std_time.d = tim->tm_mday; std_time.y = tim->tm_year + 1900; - + /* Setting the value as NULL when + * PQgetvalue returns a null terminated string.*/ + value = ((char *) value)[0]?value:NULL; mylog("copy_and_convert: field_type = %d, fctype = %d, value = '%s', cbValueMax=%d\n", field_type, fCType, (value == NULL) ? "" : value, cbValueMax); - + + if (!value) { /* @@ -2911,7 +2920,12 @@ ResolveOneParam(QueryBuild *qb) } /* store the oid */ +#ifdef USE_LIBPQ + lobj_oid = lo_creat(conn->pgconn, INV_READ | INV_WRITE); +#else lobj_oid = lo_creat(conn, INV_READ | INV_WRITE); +#endif /* USE_LIBPQ */ + if (lobj_oid == 0) { qb->errornumber = STMT_EXEC_ERROR; @@ -2920,17 +2934,27 @@ ResolveOneParam(QueryBuild *qb) } /* store the fd */ +#ifdef USE_LIBPQ + lobj_fd = lo_open(conn->pgconn, lobj_oid, INV_WRITE); +#else lobj_fd = lo_open(conn, lobj_oid, INV_WRITE); +#endif /* USE_LIBPQ */ + if (lobj_fd < 0) { qb->errornumber = STMT_EXEC_ERROR; qb->errormsg = "Couldnt open (in-line) large object for writing."; return SQL_ERROR; } +#ifdef USE_LIBPQ + retval = lo_write(conn->pgconn, lobj_fd, buffer, used); + lo_close(conn->pgconn, lobj_fd); +#else retval = lo_write(conn, lobj_fd, buffer, used); lo_close(conn, lobj_fd); +#endif /* USE_LIBPQ */ /* commit transaction if needed */ if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn)) @@ -3891,7 +3915,12 @@ convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue, } oid = ATOI32U(value); +#ifdef USE_LIBPQ + stmt->lobj_fd = lo_open(conn->pgconn, oid, INV_READ); +#else stmt->lobj_fd = lo_open(conn, oid, INV_READ); +#endif /* USE_LIBPQ*/ + if (stmt->lobj_fd < 0) { SC_set_error(stmt, STMT_EXEC_ERROR, "Couldnt open large object for reading."); @@ -3899,15 +3928,30 @@ convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue, } /* Get the size */ +#ifdef USE_LIBPQ + retval = lo_lseek(conn->pgconn, stmt->lobj_fd, 0L, SEEK_END); +#else retval = lo_lseek(conn, stmt->lobj_fd, 0L, SEEK_END); +#endif /* USE_LIBPQ*/ + if (retval >= 0) { +#ifdef USE_LIBPQ + left = lo_tell(conn->pgconn, stmt->lobj_fd); +#else left = lo_tell(conn, stmt->lobj_fd); +#endif /* USE_LIBPQ*/ + if (gdata) gdata->data_left = left; /* return to beginning */ +#ifdef USE_LIBPQ + lo_lseek(conn->pgconn, stmt->lobj_fd, 0L, SEEK_SET); +#else lo_lseek(conn, stmt->lobj_fd, 0L, SEEK_SET); +#endif /* USE_LIBPQ*/ + } } mylog("lo data left = %d\n", left); @@ -3921,10 +3965,20 @@ convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue, return COPY_GENERAL_ERROR; } +#ifdef USE_LIBPQ + retval = lo_read(conn->pgconn, stmt->lobj_fd, (char *) rgbValue, factor > 1 ? (cbValueMax - 1) / factor : cbValueMax); +#else retval = lo_read(conn, stmt->lobj_fd, (char *) rgbValue, factor > 1 ? (cbValueMax - 1) / factor : cbValueMax); +#endif /* USE_LIBPQ */ + if (retval < 0) { +#ifdef USE_LIBPQ + lo_close(conn->pgconn, stmt->lobj_fd); +#else lo_close(conn, stmt->lobj_fd); +#endif /* USE_LIBPQ */ + /* commit transaction if needed */ if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn)) @@ -3957,7 +4011,11 @@ convert_lo(StatementClass *stmt, const void *value, Int2 fCType, PTR rgbValue, if (!gdata || gdata->data_left == 0) { +#ifdef USE_LIBPQ + lo_close(conn->pgconn, stmt->lobj_fd); +#else lo_close(conn, stmt->lobj_fd); +#endif /* USE_LIBPQ */ /* commit transaction if needed */ if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(conn)) diff --git a/descriptor.c b/descriptor.c index 6e1bd81..d9a344a 100644 --- a/descriptor.c +++ b/descriptor.c @@ -12,7 +12,13 @@ */ #include "environ.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "descriptor.h" #include "statement.h" diff --git a/dlg_specific.c b/dlg_specific.c index f79d0e7..b172c6f 100644 --- a/dlg_specific.c +++ b/dlg_specific.c @@ -286,10 +286,12 @@ copyAttributes(ConnInfo *ci, const char *attribute, const char *value) else if (stricmp(attribute, INI_PORT) == 0) strcpy(ci->port, value); +#ifndef USE_LIBPQ #ifdef HAVE_UNIX_SOCKETS else if (stricmp(attribute, INI_UDS) == 0) strcpy(ci->uds, value); #endif +#endif /*USE_LIBPQ*/ else if (stricmp(attribute, INI_READONLY) == 0 || stricmp(attribute, "A0") == 0) strcpy(ci->onlyread, value); @@ -497,10 +499,12 @@ getDSNinfo(ConnInfo *ci, char overwrite) if (ci->port[0] == '\0' || overwrite) SQLGetPrivateProfileString(DSN, INI_PORT, "", ci->port, sizeof(ci->port), ODBC_INI); +#ifndef USE_LIBPQ #ifdef HAVE_UNIX_SOCKETS if (ci->uds[0] == '\0' || overwrite) SQLGetPrivateProfileString(DSN, INI_UDS, "", ci->uds, sizeof(ci->uds), ODBC_INI); #endif +#endif /* USE_LIBPQ */ if (ci->onlyread[0] == '\0' || overwrite) SQLGetPrivateProfileString(DSN, INI_READONLY, "", ci->onlyread, sizeof(ci->onlyread), ODBC_INI); @@ -735,12 +739,14 @@ writeDSNinfo(const ConnInfo *ci) ci->port, ODBC_INI); +#ifndef USE_LIBPQ #ifdef HAVE_UNIX_SOCKETS SQLWritePrivateProfileString(DSN, INI_UDS, ci->uds, ODBC_INI); #endif +#endif /* USE_LIBPQ */ SQLWritePrivateProfileString(DSN, INI_USER, diff --git a/dlg_specific.h b/dlg_specific.h index 4097e3a..11a0074 100644 --- a/dlg_specific.h +++ b/dlg_specific.h @@ -10,7 +10,12 @@ #define __DLG_SPECIFIC_H__ #include "psqlodbc.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ #ifdef WIN32 #include diff --git a/drvconn.c b/drvconn.c index 3ea1867..0a97df1 100644 --- a/drvconn.c +++ b/drvconn.c @@ -17,7 +17,11 @@ #include #include +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ #ifndef WIN32 #include diff --git a/environ.c b/environ.c index fb12a95..4a513f6 100644 --- a/environ.c +++ b/environ.c @@ -15,7 +15,12 @@ #include "environ.h" +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "dlg_specific.h" #include "statement.h" #include @@ -156,7 +161,7 @@ ER_ReturnError(PG_ErrorInfo *error, /* * Even though an application specifies a larger error message * buffer, the driver manager changes it silently. - * Therefore we divide the error message into ... + * Therefore we divide the error message into ... */ if (error->recsize < 0) { @@ -174,19 +179,19 @@ ER_ReturnError(PG_ErrorInfo *error, } stapos = (RecNumber - 1) * error->recsize; if (stapos > msglen) - return SQL_NO_DATA_FOUND; + return SQL_NO_DATA_FOUND; pcblen = wrtlen = msglen - stapos; if (pcblen > error->recsize) pcblen = error->recsize; if (0 == cbErrorMsgMax) - wrtlen = 0; + wrtlen = 0; else if (wrtlen >= cbErrorMsgMax) { if (partial_ok) wrtlen = cbErrorMsgMax - 1; else if (cbErrorMsgMax <= error->recsize) wrtlen = 0; - else + else wrtlen = error->recsize; } if (wrtlen > pcblen) @@ -263,7 +268,7 @@ PGAPI_StmtError( HSTMT hstmt, /* * Even though an application specifies a larger error message * buffer, the driver manager changes it silently. - * Therefore we divide the error message into ... + * Therefore we divide the error message into ... */ if (stmt->error_recsize < 0) { @@ -281,19 +286,19 @@ PGAPI_StmtError( HSTMT hstmt, } stapos = (RecNumber - 1) * stmt->error_recsize; if (stapos > msglen) - return SQL_NO_DATA_FOUND; + return SQL_NO_DATA_FOUND; pcblen = wrtlen = msglen - stapos; if (pcblen > stmt->error_recsize) pcblen = stmt->error_recsize; if (0 == cbErrorMsgMax) - wrtlen = 0; + wrtlen = 0; else if (wrtlen >= cbErrorMsgMax) { if (partial_ok) wrtlen = cbErrorMsgMax - 1; else if (cbErrorMsgMax <= stmt->error_recsize) wrtlen = 0; - else + else wrtlen = stmt->error_recsize; } if (wrtlen > pcblen) @@ -608,7 +613,7 @@ PGAPI_EnvError( HENV henv, if (!EN_get_error(env, &status, &msg) || NULL == msg) { mylog("EN_get_error: status = %d, msg = #%s#\n", status, msg); - + if (NULL != szSqlState) pg_sqlstate_set(env, szSqlState, "00000", "00000"); if (NULL != pcbErrorMsg) diff --git a/execute.c b/execute.c index fb6d3f0..93539e7 100644 --- a/execute.c +++ b/execute.c @@ -19,7 +19,13 @@ #include #include "environ.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "statement.h" #include "qresult.h" #include "convert.h" @@ -226,7 +232,7 @@ RETCODE Exec_with_parameters_resolved(StatementClass *stmt, BOOL *exec_end) /* * Dummy exection to get the column info. - */ + */ if (stmt->inaccurate_result && conn->connInfo.disallow_premature) { BOOL in_trans = CC_is_in_trans(conn); @@ -301,7 +307,7 @@ RETCODE Exec_with_parameters_resolved(StatementClass *stmt, BOOL *exec_end) if (res && QR_command_maybe_successful(res)) { QResultClass *kres; - + kres = res->next; SC_set_Result(stmt, kres); res->next = NULL; @@ -323,13 +329,13 @@ RETCODE Exec_with_parameters_resolved(StatementClass *stmt, BOOL *exec_end) { switch (retval) { - case SQL_SUCCESS: + case SQL_SUCCESS: ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS; break; - case SQL_SUCCESS_WITH_INFO: + case SQL_SUCCESS_WITH_INFO: ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_SUCCESS_WITH_INFO; break; - default: + default: ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_ERROR; break; } @@ -493,9 +499,9 @@ PGAPI_Execute(HSTMT hstmt, UWORD flag) } if (start_row = stmt->exec_start_row, start_row < 0) - start_row = 0; + start_row = 0; if (end_row = stmt->exec_end_row, end_row < 0) - end_row = apdopts->paramset_size - 1; + end_row = apdopts->paramset_size - 1; if (stmt->exec_current_row < 0) stmt->exec_current_row = start_row; ipdopts = SC_get_IPDF(stmt); @@ -505,7 +511,7 @@ PGAPI_Execute(HSTMT hstmt, UWORD flag) *ipdopts->param_processed_ptr = 0; /* - * Initialize the param_status_ptr + * Initialize the param_status_ptr */ if (ipdopts->param_status_ptr) { @@ -532,7 +538,7 @@ next_param_row: } } /* - * Initialize the current row status + * Initialize the current row status */ if (ipdopts->param_status_ptr) ipdopts->param_status_ptr[stmt->exec_current_row] = SQL_PARAM_ERROR; @@ -553,7 +559,7 @@ next_param_row: Int4 current_row = stmt->exec_current_row < 0 ? 0 : stmt->exec_current_row; /* - * Increment the number of currently processed rows + * Increment the number of currently processed rows */ if (ipdopts->param_processed_ptr) (*ipdopts->param_processed_ptr)++; @@ -866,7 +872,11 @@ PGAPI_ParamData( /* close the large object */ if (estmt->lobj_fd >= 0) { +#ifdef USE_LIBPQ + lo_close(estmt->hdbc->pgconn, estmt->lobj_fd); +#else lo_close(estmt->hdbc, estmt->lobj_fd); +#endif /* USE_LIBPQ */ /* commit transaction if needed */ if (!ci->drivers.use_declarefetch && CC_is_in_autocommit(estmt->hdbc)) @@ -917,7 +927,7 @@ PGAPI_ParamData( if (stmt->execute_delegate) { UInt4 offset = apdopts->param_offset_ptr ? *apdopts->param_offset_ptr : 0; - UInt4 perrow = apdopts->param_bind_type > 0 ? apdopts->param_bind_type : apdopts->parameters[i].buflen; + UInt4 perrow = apdopts->param_bind_type > 0 ? apdopts->param_bind_type : apdopts->parameters[i].buflen; *prgbValue = apdopts->parameters[i].buffer + offset + estmt->exec_current_row * perrow; } @@ -1056,7 +1066,11 @@ PGAPI_PutData( } /* store the oid */ +#ifdef USE_LIBPQ + current_pdata->lobj_oid = lo_creat(conn->pgconn, INV_READ | INV_WRITE); +#else current_pdata->lobj_oid = lo_creat(conn, INV_READ | INV_WRITE); +#endif /* USE_LIBPQ */ if (current_pdata->lobj_oid == 0) { SC_set_error(stmt, STMT_EXEC_ERROR, "Couldnt create large object."); @@ -1071,7 +1085,11 @@ PGAPI_PutData( /***current_param->EXEC_buffer = (char *) ¤t_param->lobj_oid;***/ /* store the fd */ +#ifdef USE_LIBPQ + estmt->lobj_fd = lo_open(conn->pgconn, current_pdata->lobj_oid, INV_WRITE); +#else estmt->lobj_fd = lo_open(conn, current_pdata->lobj_oid, INV_WRITE); +#endif /* USE_LIBPQ */ if (estmt->lobj_fd < 0) { SC_set_error(stmt, STMT_EXEC_ERROR, "Couldnt open large object for writing."); @@ -1079,7 +1097,11 @@ PGAPI_PutData( return SQL_ERROR; } +#ifdef USE_LIBPQ + retval = lo_write(conn->pgconn, estmt->lobj_fd, putbuf, putlen); +#else retval = lo_write(conn, estmt->lobj_fd, putbuf, putlen); +#endif /* USE_LIBPQ */ mylog("lo_write: cbValue=%d, wrote %d bytes\n", putlen, retval); } else @@ -1104,7 +1126,11 @@ PGAPI_PutData( if (current_iparam->PGType == conn->lobj_type) { /* the large object fd is in EXEC_buffer */ +#ifdef USE_LIBPQ + retval = lo_write(conn->pgconn, estmt->lobj_fd, putbuf, putlen); +#else retval = lo_write(conn, estmt->lobj_fd, putbuf, putlen); +#endif /* USE_LIBPQ */ mylog("lo_write(2): cbValue = %d, wrote %d bytes\n", putlen, retval); *current_pdata->EXEC_used += putlen; diff --git a/info.c b/info.c index 4571237..ff26d09 100644 --- a/info.c +++ b/info.c @@ -30,7 +30,13 @@ #include "dlg_specific.h" #include "environ.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "statement.h" #include "qresult.h" #include "bind.h" @@ -1218,7 +1224,7 @@ PGAPI_Tables( char show_system_tables, show_regular_tables, show_views, - remarks[254]; //Added for holding Table Description, if any. + remarks[254]; /*Added for holding Table Description, if any.*/ char regular_table, view, systable; diff --git a/info30.c b/info30.c index 06b0694..d9b9273 100644 --- a/info30.c +++ b/info30.c @@ -8,7 +8,12 @@ #include "psqlodbc.h" +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "pgapifunc.h" RETCODE SQL_API @@ -183,11 +188,11 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, len = 4; value = SQL_CT_CREATE_TABLE | SQL_CT_COLUMN_CONSTRAINT | SQL_CT_COLUMN_DEFAULT; - if (PG_VERSION_GE(conn, 6.5)) - value |= SQL_CT_GLOBAL_TEMPORARY; - if (PG_VERSION_GE(conn, 7.0)) + if (PG_VERSION_GE(conn, 6.5)) + value |= SQL_CT_GLOBAL_TEMPORARY; + if (PG_VERSION_GE(conn, 7.0)) value |= SQL_CT_TABLE_CONSTRAINT - | SQL_CT_CONSTRAINT_NAME_DEFINITION + | SQL_CT_CONSTRAINT_NAME_DEFINITION | SQL_CT_CONSTRAINT_INITIALLY_DEFERRED | SQL_CT_CONSTRAINT_INITIALLY_IMMEDIATE | SQL_CT_CONSTRAINT_DEFERRABLE; @@ -296,7 +301,7 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, break; case SQL_SQL92_NUMERIC_VALUE_FUNCTIONS: len = 4; - value = SQL_SNVF_BIT_LENGTH | SQL_SNVF_CHAR_LENGTH + value = SQL_SNVF_BIT_LENGTH | SQL_SNVF_CHAR_LENGTH | SQL_SNVF_CHARACTER_LENGTH | SQL_SNVF_EXTRACT | SQL_SNVF_OCTET_LENGTH | SQL_SNVF_POSITION; break; @@ -315,7 +320,7 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, | SQL_SRJO_FULL_OUTER_JOIN | SQL_SRJO_INNER_JOIN | SQL_SRJO_INTERSECT_JOIN | SQL_SRJO_LEFT_OUTER_JOIN | SQL_SRJO_NATURAL_JOIN | SQL_SRJO_RIGHT_OUTER_JOIN - | SQL_SRJO_UNION_JOIN; + | SQL_SRJO_UNION_JOIN; break; case SQL_SQL92_REVOKE: len = 4; @@ -369,7 +374,7 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, */ if (conn->unicode) len = len * WCLEN; - + if (rgbInfoValue) { @@ -401,7 +406,7 @@ PGAPI_GetInfo30(HDBC hdbc, UWORD fInfoType, PTR rgbInfoValue, *pcbInfoValue = len; mylog("%s: p='%s', len=%d, value=%d, cbMax=%d\n", func, p ? p : "", len, value, cbInfoValueMax); - + return result; } diff --git a/installer/Make.bat b/installer/Make.bat index 22eed5b..de715ac 100644 --- a/installer/Make.bat +++ b/installer/Make.bat @@ -3,7 +3,7 @@ if NOT "%1"=="" SET VERSION="%1" if NOT "%1"=="" GOTO GOT_VERSION -SET VERSION="08.00.0101" +SET VERSION="08.01.0001" echo. echo Version not specified - defaulting to %VERSION% echo. diff --git a/libpqconnection.h b/libpqconnection.h new file mode 100644 index 0000000..8061ea6 --- /dev/null +++ b/libpqconnection.h @@ -0,0 +1,420 @@ +/* File: libpqconnection.h + * + * Description: See "libpqconnection.c" + * + * Comments: See "notice.txt" for copyright and license information. + * + */ + +#ifdef USE_LIBPQ + +#ifndef __LIBPQCONNECTION_H__ +#define __LIBPQCONNECTION_H__ + +#include "psqlodbc.h" +#include +#include +#include +#include "descriptor.h" + +#if defined (POSIX_MULTITHREAD_SUPPORT) +#include +#endif + +#if !defined WIN32 && defined HAVE_SYS_UN_H && !defined HAVE_UNIX_SOCKETS +#define HAVE_UNIX_SOCKETS +#endif + +/* These errors have general sql error state */ +#define CONNECTION_SERVER_NOT_REACHED 101 +#define CONNECTION_MSG_TOO_LONG 103 +#define CONNECTION_COULD_NOT_SEND 104 +#define CONNECTION_NO_SUCH_DATABASE 105 +#define CONNECTION_BACKEND_CRAZY 106 +#define CONNECTION_NO_RESPONSE 107 +#define CONNECTION_SERVER_REPORTED_ERROR 108 +#define CONNECTION_COULD_NOT_RECEIVE 109 +#define CONNECTION_SERVER_REPORTED_WARNING 110 +#define CONNECTION_NEED_PASSWORD 112 +#define CONNECTION_COULD_NOT_ESTABLISH 113 + +/* These errors correspond to specific SQL states */ +#define CONN_INIREAD_ERROR 201 +#define CONN_OPENDB_ERROR 202 +#define CONN_STMT_ALLOC_ERROR 203 +#define CONN_IN_USE 204 +#define CONN_UNSUPPORTED_OPTION 205 +/* Used by SetConnectoption to indicate unsupported options */ +#define CONN_INVALID_ARGUMENT_NO 206 +/* SetConnectOption: corresponds to ODBC--"S1009" */ +#define CONN_TRANSACT_IN_PROGRES 207 +#define CONN_NO_MEMORY_ERROR 208 +#define CONN_NOT_IMPLEMENTED_ERROR 209 +#define CONN_INVALID_AUTHENTICATION 210 +#define CONN_AUTH_TYPE_UNSUPPORTED 211 +#define CONN_UNABLE_TO_LOAD_DLL 212 + +#define CONN_OPTION_VALUE_CHANGED 213 +#define CONN_VALUE_OUT_OF_RANGE 214 + +#define CONN_TRUNCATED 215 + + +#define CONN_MEMORY_ALLOCATION_FAILED 301 +#define COULD_NOT_GET_RESULT_BACK 302 + +/* Conn_status defines */ +#define CONN_IN_AUTOCOMMIT 1L +#define CONN_IN_TRANSACTION (1L<<1) +#define CONN_IN_MANUAL_TRANSACTION (1L<<2) +#define CONN_IN_ERROR_BEFORE_IDLE (1L<<3) + +/* AutoCommit functions */ +#define CC_set_autocommit_off(x) (x->transact_status &= ~CONN_IN_AUTOCOMMIT) +#define CC_set_autocommit_on(x) (x->transact_status |= CONN_IN_AUTOCOMMIT) +#define CC_is_in_autocommit(x) (x->transact_status & CONN_IN_AUTOCOMMIT) + +/* Transaction in/not functions */ +#define CC_set_in_trans(x) (x->transact_status |= CONN_IN_TRANSACTION) +#define CC_set_no_trans(x) (x->transact_status &= ~(CONN_IN_TRANSACTION | CONN_IN_ERROR_BEFORE_IDLE)) +#define CC_is_in_trans(x) (x->transact_status & CONN_IN_TRANSACTION) + +/* Manual transaction in/not functions */ +#define CC_set_in_manual_trans(x) (x->transact_status |= CONN_IN_MANUAL_TRANSACTION) +#define CC_set_no_manual_trans(x) (x->transact_status &= ~CONN_IN_MANUAL_TRANSACTION) +#define CC_is_in_manual_trans(x) (x->transact_status & CONN_IN_MANUAL_TRANSACTION) + +/* Error waiting for ROLLBACK */ +#define CC_set_in_error_trans(x) (x->transact_status |= CONN_IN_ERROR_BEFORE_IDLE) +#define CC_set_no_error_trans(x) (x->transact_status &= ~CONN_IN_ERROR_BEFORE_IDLE) +#define CC_is_in_error_trans(x) (x->transact_status & CONN_IN_ERROR_BEFORE_IDLE) + +#define CC_get_errornumber(x) (x->__error_number) +#define CC_get_errormsg(x) (x->__error_message) +#define CC_set_errornumber(x, n) (x->__error_number = n) + +#define CC_MALLOC_return_with_error(t, tp, s, x, m, ret) \ + { \ + if (t = malloc(s), NULL == t) \ + { \ + CC_set_error(x, CONN_NO_MEMORY_ERROR, m); \ + return ret; \ + } \ + } +#define CC_REALLOC_return_with_error(t, tp, s, x, m, ret) \ + { \ + if (t = (tp *) realloc(t, s), NULL == t) \ + { \ + CC_set_error(x, CONN_NO_MEMORY_ERROR, m); \ + return ret; \ + } \ + } + +/* For Multi-thread */ +#if defined(WIN_MULTITHREAD_SUPPORT) +#define INIT_CONN_CS(x) InitializeCriticalSection(&((x)->cs)) +#define ENTER_CONN_CS(x) EnterCriticalSection(&((x)->cs)) +#define ENTER_INNER_CONN_CS(x, entered) \ + { EnterCriticalSection(&((x)->cs)); entered++; } +#define LEAVE_CONN_CS(x) LeaveCriticalSection(&((x)->cs)) +#define DELETE_CONN_CS(x) DeleteCriticalSection(&((x)->cs)) +#elif defined(POSIX_THREADMUTEX_SUPPORT) +#define INIT_CONN_CS(x) pthread_mutex_init(&((x)->cs), getMutexAttr()) +#define ENTER_CONN_CS(x) pthread_mutex_lock(&((x)->cs)) +#define ENTER_INNER_CONN_CS(x, entered) \ + { \ + if (getMutexAttr()) \ + { \ + if (pthread_mutex_lock(&((x)->cs)) == 0) \ + entered++; \ + else \ + -1; \ + } \ + else \ + 0; \ + } +#define LEAVE_CONN_CS(x) pthread_mutex_unlock(&((x)->cs)) +#define DELETE_CONN_CS(x) pthread_mutex_destroy(&((x)->cs)) +#else +#define INIT_CONN_CS(x) +#define ENTER_CONN_CS(x) +#define ENTER_INNER_CONN_CS(x, entered) (0) +#define LEAVE_CONN_CS(x) +#define DELETE_CONN_CS(x) +#endif /* WIN_MULTITHREAD_SUPPORT */ + +#define LEAVE_INNER_CONN_CS(entered, conn) \ + { \ + if (entered > 0) \ + { \ + LEAVE_CONN_CS(conn); \ + entered--; \ + } \ + } +#define CLEANUP_FUNC_CONN_CS(entered, conn) \ + while (entered > 0) \ + { \ + LEAVE_CONN_CS(conn); \ + entered--; \ + } + + +#define AUTH_REQ_OK 0 +#define AUTH_REQ_KRB4 1 +#define AUTH_REQ_KRB5 2 +#define AUTH_REQ_PASSWORD 3 +#define AUTH_REQ_CRYPT 4 +#define AUTH_REQ_MD5 5 +#define AUTH_REQ_SCM_CREDS 6 + +/* Old 6.2 protocol defines */ +#define NO_AUTHENTICATION 7 +#define PATH_SIZE 64 +#define ARGV_SIZE 64 +#define USRNAMEDATALEN 16 + +typedef unsigned int ProtocolVersion; + +#define PG_PROTOCOL(major, minor) (((major) << 16) | (minor)) +#define PG_PROTOCOL_LATEST PG_PROTOCOL(2, 0) +#define PG_PROTOCOL_63 PG_PROTOCOL(1, 0) +#define PG_PROTOCOL_62 PG_PROTOCOL(0, 0) + + +typedef enum +{ + CONN_NOT_CONNECTED, /* Connection has not been established */ + CONN_CONNECTED, /* Connection is up and has been + * established */ + CONN_DOWN, /* Connection is broken */ + CONN_EXECUTING /* the connection is currently executing a + * statement */ +} CONN_Status; + +/* Transferred from pqcomm.h: */ + + +typedef ProtocolVersion MsgType; + +/* Structure to hold all the connection attributes for a specific + connection (used for both registry and file, DSN and DRIVER) */ + +typedef struct +{ + char dsn[MEDIUM_REGISTRY_LEN]; + char desc[MEDIUM_REGISTRY_LEN]; + char drivername[MEDIUM_REGISTRY_LEN]; + char server[MEDIUM_REGISTRY_LEN]; + char database[MEDIUM_REGISTRY_LEN]; + char username[MEDIUM_REGISTRY_LEN]; + char password[MEDIUM_REGISTRY_LEN]; + char conn_settings[LARGE_REGISTRY_LEN]; + char protocol[SMALL_REGISTRY_LEN]; + char port[SMALL_REGISTRY_LEN]; + char onlyread[SMALL_REGISTRY_LEN]; + char fake_oid_index[SMALL_REGISTRY_LEN]; + char show_oid_column[SMALL_REGISTRY_LEN]; + char row_versioning[SMALL_REGISTRY_LEN]; + char show_system_tables[SMALL_REGISTRY_LEN]; + char translation_dll[MEDIUM_REGISTRY_LEN]; + char translation_option[SMALL_REGISTRY_LEN]; + char focus_password; + signed char disallow_premature; + signed char allow_keyset; + signed char updatable_cursors; + signed char lf_conversion; + signed char true_is_minus1; + signed char int8_as; + signed char bytea_as_longvarbinary; + signed char use_server_side_prepare; + signed char lower_case_identifier; + GLOBAL_VALUES drivers; /* moved from driver's option */ +} ConnInfo; + + +/* Macro to determine is the connection using 6.2 protocol? */ +#define PROTOCOL_62(conninfo_) (strncmp((conninfo_)->protocol, PG62, strlen(PG62)) == 0) + +/* Macro to determine is the connection using 6.3 protocol? */ +#define PROTOCOL_63(conninfo_) (strncmp((conninfo_)->protocol, PG63, strlen(PG63)) == 0) + +/* + * Macros to compare the server's version with a specified version + * 1st parameter: pointer to a ConnectionClass object + * 2nd parameter: major version number + * 3rd parameter: minor version number + */ +#define SERVER_VERSION_GT(conn, major, minor) \ + ((conn)->pg_version_major > major || \ + ((conn)->pg_version_major == major && (conn)->pg_version_minor > minor)) +#define SERVER_VERSION_GE(conn, major, minor) \ + ((conn)->pg_version_major > major || \ + ((conn)->pg_version_major == major && (conn)->pg_version_minor >= minor)) +#define SERVER_VERSION_EQ(conn, major, minor) \ + ((conn)->pg_version_major == major && (conn)->pg_version_minor == minor) +#define SERVER_VERSION_LE(conn, major, minor) (! SERVER_VERSION_GT(conn, major, minor)) +#define SERVER_VERSION_LT(conn, major, minor) (! SERVER_VERSION_GE(conn, major, minor)) +/*#if ! defined(HAVE_CONFIG_H) || defined(HAVE_STRINGIZE)*/ +#define STRING_AFTER_DOT(string) (strchr(#string, '.') + 1) +/*#else +#define STRING_AFTER_DOT(str) (strchr("str", '.') + 1) +#endif*/ +/* + * Simplified macros to compare the server's version with a + * specified version + * Note: Never pass a variable as the second parameter. + * It must be a decimal constant of the form %d.%d . + */ +#define PG_VERSION_GT(conn, ver) \ + (SERVER_VERSION_GT(conn, (int) ver, atoi(STRING_AFTER_DOT(ver)))) +#define PG_VERSION_GE(conn, ver) \ + (SERVER_VERSION_GE(conn, (int) ver, atoi(STRING_AFTER_DOT(ver)))) +#define PG_VERSION_EQ(conn, ver) \ + (SERVER_VERSION_EQ(conn, (int) ver, atoi(STRING_AFTER_DOT(ver)))) +#define PG_VERSION_LE(conn, ver) (! PG_VERSION_GT(conn, ver)) +#define PG_VERSION_LT(conn, ver) (! PG_VERSION_GE(conn, ver)) + +/* This is used to store cached table information in the connection */ +struct col_info +{ + QResultClass *result; + char *schema; + char name[TABLE_NAME_STORAGE_LEN + 1]; +}; + + /* Translation DLL entry points */ +#ifdef WIN32 +#define DLLHANDLE HINSTANCE +#else +#define WINAPI CALLBACK +#define DLLHANDLE void * +#define HINSTANCE void * +#endif + +typedef BOOL (FAR WINAPI * DataSourceToDriverProc) + (UDWORD,SWORD,PTR,SDWORD,PTR,SDWORD,SDWORD FAR *,UCHAR FAR *,SWORD,SWORD FAR *); + +typedef BOOL (FAR WINAPI * DriverToDataSourceProc) + (UDWORD,SWORD,PTR,SDWORD,PTR,SDWORD,SDWORD FAR *,UCHAR FAR *,SWORD,SWORD FAR *); + + /******* The Connection handle ************/ +struct ConnectionClass_ +{ + HENV henv; /* environment this connection was created + * on */ + StatementOptions stmtOptions; + ARDFields ardOptions; + APDFields apdOptions; + char *__error_message; + int __error_number; + CONN_Status status; + ConnInfo connInfo; + StatementClass **stmts; + int num_stmts; + PGconn *pgconn; + int lobj_type; + int ntables; + COL_INFO **col_info; + UDWORD translation_option; + HINSTANCE translation_handle; + DataSourceToDriverProc DataSourceToDriver; + DriverToDataSourceProc DriverToDataSource; + Int2 driver_version; /* prepared for ODBC3.0 */ + char transact_status;/* Is a transaction is currently in + * progress */ + char errormsg_created; /* has an informative error msg + * been created? */ + char pg_version[MAX_INFO_STRING]; /* Version of PostgreSQL + * we're connected to - + * DJP 25-1-2001 */ + float pg_version_number; + Int2 pg_version_major; + Int2 pg_version_minor; + char ms_jet; + char unicode; + char result_uncommitted; + char schema_support; + char *client_encoding; + char *server_encoding; + int ccsc; + int be_pid; /* pid returned by backend */ + int be_key; /* auth code needed to send cancel */ + UInt4 isolation; + char *current_schema; + int num_discardp; + char **discardp; + int num_descs; + DescriptorClass **descs; +#if defined(WIN_MULTITHREAD_SUPPORT) + CRITICAL_SECTION cs; +#elif defined(POSIX_THREADMUTEX_SUPPORT) + pthread_mutex_t cs; +#endif /* WIN_MULTITHREAD_SUPPORT */ +}; + + +/* Accessor functions */ +#define CC_get_database(x) (x->connInfo.database) +#define CC_get_server(x) (x->connInfo.server) +#define CC_get_DSN(x) (x->connInfo.dsn) +#define CC_get_username(x) (x->connInfo.username) +#define CC_is_onlyread(x) (x->connInfo.onlyread[0] == '1') + +/* for CC_DSN_info */ +#define CONN_DONT_OVERWRITE 0 +#define CONN_OVERWRITE 1 + + +/* prototypes */ +ConnectionClass *CC_Constructor(void); +void CC_conninfo_init(ConnInfo *conninfo); +char CC_Destructor(ConnectionClass *self); +int CC_cursor_count(ConnectionClass *self); +char CC_cleanup(ConnectionClass *self); +char CC_begin(ConnectionClass *self); +char CC_commit(ConnectionClass *self); +char CC_abort(ConnectionClass *self); +int CC_set_translation(ConnectionClass *self); +char CC_connect(ConnectionClass *self, char password_req, char *salt); +char CC_add_statement(ConnectionClass *self, StatementClass *stmt); +char CC_remove_statement(ConnectionClass *self, StatementClass *stmt); +char CC_add_descriptor(ConnectionClass *self, DescriptorClass *desc); +char CC_remove_descriptor(ConnectionClass *self, DescriptorClass *desc); +void CC_set_error(ConnectionClass *self, int number, const char *message); +void CC_set_errormsg(ConnectionClass *self, const char *message); +char CC_get_error(ConnectionClass *self, int *number, char **message); +QResultClass *CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi, UDWORD flag); +void CC_clear_error(ConnectionClass *self); +char *CC_create_errormsg(ConnectionClass *self); +int CC_send_function(ConnectionClass *conn, int fnid, void *result_buf, int *actual_result_len, int result_is_int, LO_ARG *argv, int nargs); +char CC_send_settings(ConnectionClass *self); +void CC_lookup_lo(ConnectionClass *conn); +void CC_lookup_pg_version(ConnectionClass *conn); +void CC_initialize_pg_version(ConnectionClass *conn); +void CC_log_error(const char *func, const char *desc, const ConnectionClass *self); +int CC_get_max_query_len(const ConnectionClass *self); +int CC_send_cancel_request(const ConnectionClass *conn); +void CC_on_commit(ConnectionClass *conn); +void CC_on_abort(ConnectionClass *conn, UDWORD opt); +void ProcessRollback(ConnectionClass *conn, BOOL undo); +const char *CC_get_current_schema(ConnectionClass *conn); +int CC_mark_a_plan_to_discard(ConnectionClass *conn, const char *plannm); +int CC_discard_marked_plans(ConnectionClass *conn); + +/* Accessor functions*/ +PGconn *LIBPQ_Constructor(); +void LIBPQ_Destructor(PGconn *pgconn); +int LIBPQ_connect(ConnectionClass *self); +QResultClass *LIBPQ_execute_query(ConnectionClass *self,char *query); +QResultClass *CC_mapping(PGresult *pgres,QResultClass *qres); +/* CC_send_query options */ +#define CLEAR_RESULT_ON_ABORT 1L +#define CREATE_KEYSET (1L << 1) /* create keyset for updatable curosrs */ +#define GO_INTO_TRANSACTION (1L << 2) /* issue begin in advance */ +/* CC_on_abort options */ +#define NO_TRANS 1L +#define CONN_DEAD (1L << 1) /* connection is no longer valid */ + +#endif /* __LIBPQCONNECTION_H__ */ + +#endif /* USE_LIBPQ */ diff --git a/lobj.c b/lobj.c index 6b55b82..eef659c 100644 --- a/lobj.c +++ b/lobj.c @@ -12,6 +12,8 @@ *-------- */ +#ifndef USE_LIBPQ + #include "lobj.h" #include "connection.h" @@ -184,3 +186,5 @@ lo_unlink(ConnectionClass *conn, Oid lobjId) else return retval; } + +#endif /* USE_LIBPQ */ diff --git a/lobj.h b/lobj.h index 4d72048..646274b 100644 --- a/lobj.h +++ b/lobj.h @@ -35,6 +35,8 @@ struct lo_arg #define INV_WRITE 0x00020000 #define INV_READ 0x00040000 +#ifndef USE_LIBPQ + Oid lo_creat(ConnectionClass *conn, int mode); int lo_open(ConnectionClass *conn, int lobjId, int mode); int lo_close(ConnectionClass *conn, int fd); @@ -44,4 +46,6 @@ int lo_lseek(ConnectionClass *conn, int fd, int offset, int len); int lo_tell(ConnectionClass *conn, int fd); int lo_unlink(ConnectionClass *conn, Oid lobjId); +#endif /*USE_LIBPQ */ + #endif diff --git a/misc.c b/misc.c index 4589f19..35d6756 100644 --- a/misc.c +++ b/misc.c @@ -26,7 +26,13 @@ #include /* Byron: is this where Windows keeps def. * of getpid ? */ #endif + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "multibyte.h" extern GLOBAL_VALUES globals; @@ -325,7 +331,7 @@ make_lstring_ifneeded(ConnectionClass *conn, const char *s, int len, BOOL ifallu str = NULL; } break; - } + } if (tolower(*ptr) != *ptr) { if (!str) diff --git a/multibyte.c b/multibyte.c index 2a47fd0..a7df17c 100644 --- a/multibyte.c +++ b/multibyte.c @@ -9,7 +9,13 @@ */ #include "multibyte.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "pgapifunc.h" #include "qresult.h" #include @@ -194,11 +200,11 @@ pg_CS_stat(int stat,unsigned int character,int characterset_code) /* EUC_JP Support */ case EUC_JP: { - if (stat < 3 && + if (stat < 3 && character == 0x8f) /* JIS X 0212 */ stat = 3; else - if (stat != 2 && + if (stat != 2 && (character == 0x8e || character > 0xa0)) /* Half Katakana HighByte & Kanji HighByte */ stat = 2; @@ -280,7 +286,7 @@ pg_mbschr(int csc, const UCHAR *string, unsigned int character) int mb_st = 0; const UCHAR *s, *rs = NULL; - for(s = string; *s ; s++) + for(s = string; *s ; s++) { mb_st = pg_CS_stat(mb_st, (UCHAR) *s, csc); if (mb_st == 0 && (*s == character)) @@ -385,7 +391,7 @@ CC_lookup_characterset(ConnectionClass *self) if (stricmp(pg_CS_name(self->ccsc), encstr)) { qlog(" Client encoding = '%s' and %s\n", self->client_encoding, pg_CS_name(self->ccsc)); - CC_set_error(self, CONN_VALUE_OUT_OF_RANGE, "client encoding mismatch"); + CC_set_error(self, CONN_VALUE_OUT_OF_RANGE, "client encoding mismatch"); } } else @@ -406,15 +412,15 @@ int encoded_nextchar(encoded_str *encstr) { int chr; - chr = encstr->encstr[++encstr->pos]; + chr = encstr->encstr[++encstr->pos]; encstr->ccst = pg_CS_stat(encstr->ccst, (unsigned int) chr, encstr->ccsc); - return chr; + return chr; } int encoded_byte_check(encoded_str *encstr, int abspos) { int chr; - chr = encstr->encstr[encstr->pos = abspos]; + chr = encstr->encstr[encstr->pos = abspos]; encstr->ccst = pg_CS_stat(encstr->ccst, (unsigned int) chr, encstr->ccsc); - return chr; + return chr; } diff --git a/odbcapi.c b/odbcapi.c index d75048d..025a4b4 100644 --- a/odbcapi.c +++ b/odbcapi.c @@ -32,7 +32,13 @@ #include "pgapifunc.h" #include "environ.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "statement.h" #include "qresult.h" @@ -124,7 +130,7 @@ SQLColumns(HSTMT StatementHandle, ret = PGAPI_Columns(StatementHandle, ctName, NameLength1, scName, NameLength2, tbName, NameLength3, clName, NameLength4, flag); - if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) + if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) { BOOL ifallupper = TRUE, reexec = FALSE; char *newCt = NULL, *newSc = NULL, *newTb = NULL, *newCl = NULL; @@ -141,7 +147,7 @@ SQLColumns(HSTMT StatementHandle, { scName = newSc; reexec = TRUE; - } + } if (newTb = make_lstring_ifneeded(conn, TableName, NameLength3, ifallupper)) { tbName = newTb; @@ -594,7 +600,7 @@ SQLSpecialColumns(HSTMT StatementHandle, ret = PGAPI_SpecialColumns(StatementHandle, IdentifierType, ctName, NameLength1, scName, NameLength2, tbName, NameLength3, Scope, Nullable); - if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) + if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) { BOOL ifallupper = TRUE, reexec = FALSE; char *newCt =NULL, *newSc = NULL, *newTb = NULL; @@ -655,7 +661,7 @@ SQLStatistics(HSTMT StatementHandle, ret = PGAPI_Statistics(StatementHandle, ctName, NameLength1, scName, NameLength2, tbName, NameLength3, Unique, Reserved); - if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) + if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) { BOOL ifallupper = TRUE, reexec = FALSE; char *newCt =NULL, *newSc = NULL, *newTb = NULL; @@ -689,7 +695,7 @@ SQLStatistics(HSTMT StatementHandle, free(newSc); if (newTb) free(newTb); - } + } } LEAVE_STMT_CS(stmt); return ret; @@ -716,7 +722,7 @@ SQLTables(HSTMT StatementHandle, ret = PGAPI_Tables(StatementHandle, ctName, NameLength1, scName, NameLength2, tbName, NameLength3, TableType, NameLength4); - if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) + if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) { BOOL ifallupper = TRUE, reexec = FALSE; char *newCt =NULL, *newSc = NULL, *newTb = NULL; @@ -783,7 +789,7 @@ SQLColumnPrivileges( ret = PGAPI_ColumnPrivileges(hstmt, ctName, cbCatalogName, scName, cbSchemaName, tbName, cbTableName, clName, cbColumnName); - if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) + if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) { BOOL ifallupper = TRUE, reexec = FALSE; char *newCt = NULL, *newSc = NULL, *newTb = NULL, *newCl = NULL; @@ -904,7 +910,7 @@ SQLForeignKeys( pkscName, cbPkSchemaName, pktbName, cbPkTableName, fkctName, cbFkCatalogName, fkscName, cbFkSchemaName, fktbName, cbFkTableName); - if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) + if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) { BOOL ifallupper = TRUE, reexec = FALSE; char *newPkct = NULL, *newPksc = NULL, *newPktb = NULL, @@ -1059,7 +1065,7 @@ SQLPrimaryKeys( else ret = PGAPI_PrimaryKeys(hstmt, ctName, cbCatalogName, scName, cbSchemaName, tbName, cbTableName); - if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) + if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) { BOOL ifallupper = TRUE, reexec = FALSE; char *newCt = NULL, *newSc = NULL, *newTb = NULL; @@ -1125,7 +1131,7 @@ SQLProcedureColumns( ret = PGAPI_ProcedureColumns(hstmt, ctName, cbCatalogName, scName, cbSchemaName, prName, cbProcName, clName, cbColumnName); - if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) + if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) { BOOL ifallupper = TRUE, reexec = FALSE; char *newCt = NULL, *newSc = NULL, *newPr = NULL, *newCl = NULL; @@ -1196,7 +1202,7 @@ SQLProcedures( else ret = PGAPI_Procedures(hstmt, ctName, cbCatalogName, scName, cbSchemaName, prName, cbProcName); - if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) + if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) { BOOL ifallupper = TRUE, reexec = FALSE; char *newCt = NULL, *newSc = NULL, *newPr = NULL; @@ -1277,7 +1283,7 @@ SQLTablePrivileges( else ret = PGAPI_TablePrivileges(hstmt, ctName, cbCatalogName, scName, cbSchemaName, tbName, cbTableName, 0); - if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) + if (SQL_SUCCESS == ret && 0 == QR_get_num_total_tuples(SC_get_Result(stmt))) { BOOL ifallupper = TRUE, reexec = FALSE; char *newCt = NULL, *newSc = NULL, *newTb = NULL; diff --git a/odbcapi30.c b/odbcapi30.c index aceaf8a..7029470 100644 --- a/odbcapi30.c +++ b/odbcapi30.c @@ -24,7 +24,13 @@ #include #include "environ.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "statement.h" #include "pgapifunc.h" @@ -456,7 +462,7 @@ SQLSetEnvAttr(HENV EnvironmentHandle, ret = SQL_SUCCESS; else ret = SQL_SUCCESS_WITH_INFO; - + break; default: env->errornumber = CONN_INVALID_ARGUMENT_NO; @@ -556,7 +562,7 @@ PGAPI_GetFunctions30(HDBC hdbc, UWORD fFunction, UWORD FAR * pfExists) if (ci->drivers.lie) SQL_FUNC_ESET(pfExists, SQL_API_SQLBROWSECONNECT); /* 55 not implmented yet */ if (ci->drivers.lie) - SQL_FUNC_ESET(pfExists, SQL_API_SQLCOLUMNPRIVILEGES); /* 56 not implemented yet */ + SQL_FUNC_ESET(pfExists, SQL_API_SQLCOLUMNPRIVILEGES); /* 56 not implemented yet */ SQL_FUNC_ESET(pfExists, SQL_API_SQLDATASOURCES); /* 57 */ if (ci->drivers.lie) SQL_FUNC_ESET(pfExists, SQL_API_SQLDESCRIBEPARAM); /* 58 not properly implemented */ @@ -572,7 +578,7 @@ PGAPI_GetFunctions30(HDBC hdbc, UWORD fFunction, UWORD FAR * pfExists) SQL_FUNC_ESET(pfExists, SQL_API_SQLNUMPARAMS); /* 63 */ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLPARAMOPTIONS); 64 deprecated */ SQL_FUNC_ESET(pfExists, SQL_API_SQLPRIMARYKEYS); /* 65 */ - SQL_FUNC_ESET(pfExists, SQL_API_SQLPROCEDURECOLUMNS); /* 66 */ + SQL_FUNC_ESET(pfExists, SQL_API_SQLPROCEDURECOLUMNS); /* 66 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLPROCEDURES); /* 67 */ SQL_FUNC_ESET(pfExists, SQL_API_SQLSETPOS); /* 68 */ /* SQL_FUNC_ESET(pfExists, SQL_API_SQLSETSCROLLOPTIONS); 69 deprecated */ @@ -631,5 +637,5 @@ SQLBulkOperations(HSTMT hstmt, SQLSMALLINT operation) #endif /* DRIVER_CURSOR_IMPLEMENT */ LEAVE_STMT_CS(stmt); return ret; -} +} diff --git a/odbcapi30w.c b/odbcapi30w.c index 5388304..ac8c05a 100644 --- a/odbcapi30w.c +++ b/odbcapi30w.c @@ -18,7 +18,13 @@ #include #include "pgapifunc.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "statement.h" @@ -91,7 +97,7 @@ RETCODE SQL_API SQLSetConnectAttrW(HDBC hdbc, /* new function */ RETCODE SQL_API SQLSetDescFieldW(SQLHDESC DescriptorHandle, SQLSMALLINT RecNumber, - SQLSMALLINT FieldIdentifier, PTR Value, + SQLSMALLINT FieldIdentifier, PTR Value, SQLINTEGER BufferLength) { RETCODE ret; @@ -232,7 +238,7 @@ RETCODE SQL_API SQLColAttributeW( SQLUSMALLINT iCol, SQLUSMALLINT iField, SQLPOINTER pCharAttr, - SQLSMALLINT cbCharAttrMax, + SQLSMALLINT cbCharAttrMax, SQLSMALLINT *pcbCharAttr, SQLPOINTER pNumAttr) { @@ -245,7 +251,7 @@ RETCODE SQL_API SQLColAttributeW( ENTER_STMT_CS((StatementClass *) hstmt); SC_clear_error((StatementClass *) hstmt); switch (iField) - { + { case SQL_DESC_BASE_COLUMN_NAME: case SQL_DESC_BASE_TABLE_NAME: case SQL_DESC_CATALOG_NAME: @@ -308,7 +314,7 @@ RETCODE SQL_API SQLGetDiagFieldW( mylog("[[SQLGetDiagFieldW]] Handle=(%u,%x) Rec=%d Id=%d\n", fHandleType, handle, iRecord, fDiagField); switch (fDiagField) - { + { case SQL_DIAG_DYNAMIC_FUNCTION: case SQL_DIAG_CLASS_ORIGIN: case SQL_DIAG_CONNECTION_NAME: diff --git a/odbcapiw.c b/odbcapiw.c index b4270dd..29658d0 100644 --- a/odbcapiw.c +++ b/odbcapiw.c @@ -22,7 +22,11 @@ #include #include "pgapifunc.h" +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ #include "statement.h" RETCODE SQL_API SQLColumnsW(HSTMT StatementHandle, @@ -36,7 +40,7 @@ RETCODE SQL_API SQLColumnsW(HSTMT StatementHandle, UInt4 nmlen1, nmlen2, nmlen3, nmlen4; StatementClass *stmt = (StatementClass *) StatementHandle; ConnectionClass *conn; - BOOL lower_id; + BOOL lower_id; mylog("[SQLColumnsW]"); conn = SC_get_conn(stmt); @@ -70,7 +74,7 @@ RETCODE SQL_API SQLConnectW(HDBC ConnectionHandle, char *svName, *usName, *auth; UInt4 nmlen1, nmlen2, nmlen3; RETCODE ret; - + mylog("[SQLConnectW]"); ENTER_CONN_CS((ConnectionClass *) ConnectionHandle); ((ConnectionClass *) ConnectionHandle)->unicode = 1; @@ -202,7 +206,7 @@ RETCODE SQL_API SQLDescribeColW(HSTMT StatementHandle, *NameLength = nmcount; } LEAVE_STMT_CS((StatementClass *) StatementHandle); - free(clName); + free(clName); return ret; } @@ -236,7 +240,7 @@ RETCODE SQL_API SQLGetCursorNameW(HSTMT StatementHandle, crName = malloc(buflen); ENTER_STMT_CS((StatementClass *) StatementHandle); ret = PGAPI_GetCursorName(StatementHandle, crName, buflen, &clen); - if (ret == SQL_SUCCESS) + if (ret == SQL_SUCCESS) { UInt4 nmcount = utf8_to_ucs2(crName, (Int4) clen, CursorName, BufferLength); if (nmcount > (UInt4) BufferLength) @@ -329,7 +333,7 @@ RETCODE SQL_API SQLSpecialColumnsW(HSTMT StatementHandle, StatementClass *stmt = (StatementClass *) StatementHandle; ConnectionClass *conn; BOOL lower_id; - + mylog("[SQLSpecialColumnsW]"); conn = SC_get_conn(stmt); lower_id = SC_is_lower_case(stmt, conn); @@ -435,7 +439,7 @@ RETCODE SQL_API SQLColumnPrivilegesW( UInt4 nmlen1, nmlen2, nmlen3, nmlen4; StatementClass *stmt = (StatementClass *) hstmt; ConnectionClass *conn; - BOOL lower_id; + BOOL lower_id; mylog("[SQLColumnPrivilegesW]"); conn = SC_get_conn(stmt); @@ -480,7 +484,7 @@ RETCODE SQL_API SQLForeignKeysW( UInt4 nmlen1, nmlen2, nmlen3, nmlen4, nmlen5, nmlen6; StatementClass *stmt = (StatementClass *) hstmt; ConnectionClass *conn; - BOOL lower_id; + BOOL lower_id; mylog("[SQLForeignKeysW]"); conn = SC_get_conn(stmt); @@ -567,7 +571,7 @@ RETCODE SQL_API SQLPrimaryKeysW( UInt4 nmlen1, nmlen2, nmlen3; StatementClass *stmt = (StatementClass *) hstmt; ConnectionClass *conn; - BOOL lower_id; + BOOL lower_id; mylog("[SQLPrimaryKeysW]"); conn = SC_get_conn(stmt); @@ -604,7 +608,7 @@ RETCODE SQL_API SQLProcedureColumnsW( UInt4 nmlen1, nmlen2, nmlen3, nmlen4; StatementClass *stmt = (StatementClass *) hstmt; ConnectionClass *conn; - BOOL lower_id; + BOOL lower_id; mylog("[SQLProcedureColumnsW]"); conn = SC_get_conn(stmt); @@ -643,7 +647,7 @@ RETCODE SQL_API SQLProceduresW( UInt4 nmlen1, nmlen2, nmlen3; StatementClass *stmt = (StatementClass *) hstmt; ConnectionClass *conn; - BOOL lower_id; + BOOL lower_id; mylog("[SQLProceduresW]"); conn = SC_get_conn(stmt); @@ -678,7 +682,7 @@ RETCODE SQL_API SQLTablePrivilegesW( UInt4 nmlen1, nmlen2, nmlen3; StatementClass *stmt = (StatementClass *) hstmt; ConnectionClass *conn; - BOOL lower_id; + BOOL lower_id; mylog("[SQLTablePrivilegesW]"); conn = SC_get_conn(stmt); diff --git a/options.c b/options.c index 7875c63..4679d9b 100644 --- a/options.c +++ b/options.c @@ -17,7 +17,13 @@ #include #include "environ.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "statement.h" #include "qresult.h" #include "pgapifunc.h" diff --git a/parse.c b/parse.c index 452e7d0..173dbca 100644 --- a/parse.c +++ b/parse.c @@ -29,7 +29,13 @@ #include #include "statement.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "qresult.h" #include "pgtypes.h" #include "pgapifunc.h" @@ -353,8 +359,6 @@ parse_statement(StatementClass *stmt) BOOL updatable = TRUE; QResultClass* resultClass; - HSTMT privStmt; - mylog("%s: entering...\n", func); diff --git a/pgapi30.c b/pgapi30.c index 5c07bdb..f4965c9 100644 --- a/pgapi30.c +++ b/pgapi30.c @@ -20,7 +20,13 @@ #include #include "environ.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "statement.h" #include "descriptor.h" #include "qresult.h" @@ -68,7 +74,7 @@ PGAPI_GetDiagRec(SQLSMALLINT HandleType, SQLHANDLE Handle, } /* - * Minimal implementation. + * Minimal implementation. * */ RETCODE SQL_API @@ -102,15 +108,15 @@ PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, case SQL_DIAG_MESSAGE_TEXT: ret = PGAPI_EnvError(Handle, RecNumber, NULL, NULL, DiagInfoPtr, - BufferLength, StringLengthPtr, 0); + BufferLength, StringLengthPtr, 0); break; case SQL_DIAG_NATIVE: ret = PGAPI_EnvError(Handle, RecNumber, NULL, DiagInfoPtr, NULL, - 0, NULL, 0); - if (StringLengthPtr) - *StringLengthPtr = sizeof(SQLINTEGER); - if (SQL_SUCCESS_WITH_INFO == ret) + 0, NULL, 0); + if (StringLengthPtr) + *StringLengthPtr = sizeof(SQLINTEGER); + if (SQL_SUCCESS_WITH_INFO == ret) ret = SQL_SUCCESS; break; case SQL_DIAG_NUMBER: @@ -130,9 +136,9 @@ PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, ret = PGAPI_EnvError(Handle, RecNumber, DiagInfoPtr, NULL, NULL, 0, NULL, 0); - if (StringLengthPtr) - *StringLengthPtr = 5; - if (SQL_SUCCESS_WITH_INFO == ret) + if (StringLengthPtr) + *StringLengthPtr = 5; + if (SQL_SUCCESS_WITH_INFO == ret) ret = SQL_SUCCESS; break; case SQL_DIAG_RETURNCODE: /* driver manager returns */ @@ -166,15 +172,15 @@ PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, case SQL_DIAG_MESSAGE_TEXT: ret = PGAPI_ConnectError(Handle, RecNumber, NULL, NULL, DiagInfoPtr, - BufferLength, StringLengthPtr, 0); + BufferLength, StringLengthPtr, 0); break; case SQL_DIAG_NATIVE: ret = PGAPI_ConnectError(Handle, RecNumber, NULL, DiagInfoPtr, NULL, - 0, NULL, 0); - if (StringLengthPtr) - *StringLengthPtr = sizeof(SQLINTEGER); - if (SQL_SUCCESS_WITH_INFO == ret) + 0, NULL, 0); + if (StringLengthPtr) + *StringLengthPtr = sizeof(SQLINTEGER); + if (SQL_SUCCESS_WITH_INFO == ret) ret = SQL_SUCCESS; break; case SQL_DIAG_NUMBER: @@ -189,14 +195,14 @@ PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, *StringLengthPtr = sizeof(SQLINTEGER); ret = SQL_SUCCESS; } - break; + break; case SQL_DIAG_SQLSTATE: ret = PGAPI_ConnectError(Handle, RecNumber, DiagInfoPtr, NULL, NULL, 0, NULL, 0); - if (StringLengthPtr) - *StringLengthPtr = 5; - if (SQL_SUCCESS_WITH_INFO == ret) + if (StringLengthPtr) + *StringLengthPtr = 5; + if (SQL_SUCCESS_WITH_INFO == ret) ret = SQL_SUCCESS; break; case SQL_DIAG_RETURNCODE: /* driver manager returns */ @@ -230,15 +236,15 @@ PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, case SQL_DIAG_MESSAGE_TEXT: ret = PGAPI_StmtError(Handle, RecNumber, NULL, NULL, DiagInfoPtr, - BufferLength, StringLengthPtr, 0); + BufferLength, StringLengthPtr, 0); break; case SQL_DIAG_NATIVE: ret = PGAPI_StmtError(Handle, RecNumber, NULL, DiagInfoPtr, NULL, - 0, NULL, 0); - if (StringLengthPtr) - *StringLengthPtr = sizeof(SQLINTEGER); - if (SQL_SUCCESS_WITH_INFO == ret) + 0, NULL, 0); + if (StringLengthPtr) + *StringLengthPtr = sizeof(SQLINTEGER); + if (SQL_SUCCESS_WITH_INFO == ret) ret = SQL_SUCCESS; break; case SQL_DIAG_NUMBER: @@ -265,9 +271,9 @@ PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, ret = PGAPI_StmtError(Handle, RecNumber, DiagInfoPtr, NULL, NULL, 0, NULL, 0); - if (StringLengthPtr) + if (StringLengthPtr) *StringLengthPtr = 5; - if (SQL_SUCCESS_WITH_INFO == ret) + if (SQL_SUCCESS_WITH_INFO == ret) ret = SQL_SUCCESS; break; case SQL_DIAG_CURSOR_ROW_COUNT: @@ -279,7 +285,7 @@ PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, if (res && QR_NumResultCols(res) > 0 && !SC_is_fetchcursor(stmt)) rc = QR_get_num_total_tuples(res) - res->dl_count; - } + } *((SQLINTEGER *) DiagInfoPtr) = rc; if (StringLengthPtr) *StringLengthPtr = sizeof(SQLINTEGER); @@ -309,7 +315,7 @@ PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, } break; case SQL_HANDLE_DESC: - conn = DC_get_conn(((DescriptorClass *) Handle)); + conn = DC_get_conn(((DescriptorClass *) Handle)); switch (DiagIdentifier) { case SQL_DIAG_CLASS_ORIGIN: @@ -333,9 +339,9 @@ PGAPI_GetDiagField(SQLSMALLINT HandleType, SQLHANDLE Handle, ret = PGAPI_DescError(Handle, RecNumber, DiagInfoPtr, NULL, NULL, 0, NULL, 0); - if (StringLengthPtr) + if (StringLengthPtr) *StringLengthPtr = 5; - if (SQL_SUCCESS_WITH_INFO == ret) + if (SQL_SUCCESS_WITH_INFO == ret) ret = SQL_SUCCESS; break; case SQL_DIAG_RETURNCODE: /* driver manager returns */ @@ -393,7 +399,7 @@ PGAPI_GetConnectAttr(HDBC ConnectionHandle, } static SQLHDESC -descHandleFromStatementHandle(HSTMT StatementHandle, SQLINTEGER descType) +descHandleFromStatementHandle(HSTMT StatementHandle, SQLINTEGER descType) { StatementClass *stmt = (StatementClass *) StatementHandle; @@ -447,7 +453,7 @@ ARDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, { case SQL_DESC_ARRAY_SIZE: opts->size_of_rowset = (SQLUINTEGER) Value; - return ret; + return ret; case SQL_DESC_ARRAY_STATUS_PTR: opts->row_operation_ptr = Value; return ret; @@ -485,7 +491,7 @@ ARDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, tptr = bookmark->used; if (Value != tptr) { - DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, "INDICATOR != OCTET_LENGTH_PTR"); + DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, "INDICATOR != OCTET_LENGTH_PTR"); ret = SQL_ERROR; } break; @@ -538,7 +544,7 @@ ARDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, if (Value != tptr) { ret = SQL_ERROR; - DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, "INDICATOR != OCTET_LENGTH_PTR"); + DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, "INDICATOR != OCTET_LENGTH_PTR"); } break; case SQL_DESC_OCTET_LENGTH_PTR: @@ -559,7 +565,7 @@ ARDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_NUM_PREC_RADIX: default:ret = SQL_ERROR; DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, - "invalid descriptor identifier"); + "invalid descriptor identifier"); } return ret; } @@ -622,7 +628,7 @@ APDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, { case SQL_DESC_ARRAY_SIZE: opts->paramset_size = (SQLUINTEGER) Value; - return ret; + return ret; case SQL_DESC_ARRAY_STATUS_PTR: opts->param_operation_ptr = Value; return ret; @@ -634,7 +640,7 @@ APDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, return ret; case SQL_DESC_COUNT: parameter_bindings_set(opts, (SQLUINTEGER) Value, FALSE); - return ret; + return ret; case SQL_DESC_TYPE: case SQL_DESC_DATETIME_INTERVAL_CODE: @@ -648,7 +654,7 @@ APDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, "bad parameter number"); return SQL_ERROR; } - para_idx = RecNumber - 1; + para_idx = RecNumber - 1; switch (FieldIdentifier) { case SQL_DESC_TYPE: @@ -687,7 +693,7 @@ APDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, if (Value != opts->parameters[para_idx].used) { ret = SQL_ERROR; - DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, "INDICATOR != OCTET_LENGTH_PTR"); + DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, "INDICATOR != OCTET_LENGTH_PTR"); } break; case SQL_DESC_OCTET_LENGTH: @@ -708,7 +714,7 @@ APDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_NUM_PREC_RADIX: default:ret = SQL_ERROR; DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, - "invaid descriptor identifier"); + "invaid descriptor identifier"); } return ret; } @@ -764,7 +770,7 @@ IRDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_UPDATABLE: /* read-only */ default:ret = SQL_ERROR; DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, - "invalid descriptor identifier"); + "invalid descriptor identifier"); } return ret; } @@ -785,7 +791,7 @@ IPDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_ROWS_PROCESSED_PTR: ipdopts->param_processed_ptr = (UInt4 *) Value; return ret; - case SQL_DESC_UNNAMED: /* only SQL_UNNAMED is allowed */ + case SQL_DESC_UNNAMED: /* only SQL_UNNAMED is allowed */ if (SQL_UNNAMED != (SQLUINTEGER) Value) { ret = SQL_ERROR; @@ -846,7 +852,7 @@ IPDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_SCALE: ipdopts->parameters[para_idx].decimal_digits = (Int2) ((Int4) Value); break; - case SQL_DESC_ALLOC_TYPE: /* read-only */ + case SQL_DESC_ALLOC_TYPE: /* read-only */ case SQL_DESC_CASE_SENSITIVE: /* read-only */ case SQL_DESC_DATETIME_INTERVAL_PRECISION: case SQL_DESC_FIXED_PREC_SCALE: /* read-only */ @@ -864,7 +870,7 @@ IPDSetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_UNSIGNED: /* read-only */ default:ret = SQL_ERROR; DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, - "invalid descriptor identifier"); + "invalid descriptor identifier"); } return ret; } @@ -929,7 +935,7 @@ ARDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, { case SQL_DESC_ARRAY_SIZE: ival = opts->size_of_rowset; - break; + break; case SQL_DESC_ARRAY_STATUS_PTR: rettype = SQL_IS_POINTER; ptr = opts->row_operation_ptr; @@ -1010,7 +1016,7 @@ ARDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_LENGTH: default:ret = SQL_ERROR; DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, - "invalid descriptor identifier"); + "invalid descriptor identifier"); } switch (rettype) { @@ -1024,7 +1030,7 @@ ARDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, *((void **) Value) = ptr; break; } - + if (StringLength) *StringLength = len; return ret; @@ -1049,13 +1055,13 @@ APDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_BIND_OFFSET_PTR: case SQL_DESC_BIND_TYPE: case SQL_DESC_COUNT: - break; + break; default:if (RecNumber <= 0 || RecNumber > opts->allocated) { DC_set_error(desc, STMT_BAD_PARAMETER_NUMBER_ERROR, "bad parameter number"); return SQL_ERROR; - } + } } para_idx = RecNumber - 1; switch (FieldIdentifier) @@ -1063,7 +1069,7 @@ APDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_ARRAY_SIZE: rettype = SQL_IS_POINTER; ival = opts->paramset_size; - break; + break; case SQL_DESC_ARRAY_STATUS_PTR: rettype = SQL_IS_POINTER; ptr = opts->param_operation_ptr; @@ -1125,7 +1131,7 @@ APDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, break; case SQL_DESC_COUNT: ival = opts->allocated; - break; + break; case SQL_DESC_ALLOC_TYPE: /* read-only */ if (desc->embedded) ival = SQL_DESC_ALLOC_AUTO; @@ -1145,7 +1151,7 @@ APDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_LENGTH: default:ret = SQL_ERROR; DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, - "invalid descriptor identifer"); + "invalid descriptor identifer"); } switch (rettype) { @@ -1159,7 +1165,7 @@ APDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, *((void **) Value) = ptr; break; } - + if (StringLength) *StringLength = len; return ret; @@ -1226,10 +1232,10 @@ IRDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_TYPE_NAME: /* read-only */ rettype = SQL_NTS; bCallColAtt = TRUE; - break; + break; default:ret = SQL_ERROR; DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, - "invalid descriptor identifier"); + "invalid descriptor identifier"); } if (bCallColAtt) { @@ -1241,7 +1247,7 @@ IRDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, FieldIdentifier, Value, (SQLSMALLINT) BufferLength, &pcbL, &ival); len = pcbL; - } + } switch (rettype) { case 0: @@ -1258,7 +1264,7 @@ IRDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, *((void **) Value) = ptr; break; } - + if (StringLength) *StringLength = len; return ret; @@ -1280,7 +1286,7 @@ IPDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_ARRAY_STATUS_PTR: case SQL_DESC_ROWS_PROCESSED_PTR: case SQL_DESC_COUNT: - break; + break; default:if (RecNumber <= 0 || RecNumber > ipdopts->allocated) { DC_set_error(desc, STMT_BAD_PARAMETER_NUMBER_ERROR, @@ -1334,7 +1340,7 @@ IPDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, break; case SQL_DESC_COUNT: ival = ipdopts->allocated; - break; + break; case SQL_DESC_PARAMETER_TYPE: ival = ipdopts->parameters[para_idx].paramType; break; @@ -1359,7 +1365,7 @@ IPDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, break; case SQL_DESC_ALLOC_TYPE: /* read-only */ ival = SQL_DESC_ALLOC_AUTO; - break; + break; case SQL_DESC_CASE_SENSITIVE: /* read-only */ case SQL_DESC_DATETIME_INTERVAL_PRECISION: case SQL_DESC_FIXED_PREC_SCALE: /* read-only */ @@ -1376,7 +1382,7 @@ IPDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, case SQL_DESC_UNSIGNED: /* read-only */ default:ret = SQL_ERROR; DC_set_error(desc, STMT_INVALID_DESCRIPTOR_IDENTIFIER, - "invalid descriptor identifier"); + "invalid descriptor identifier"); } switch (rettype) { @@ -1390,7 +1396,7 @@ IPDGetField(DescriptorClass *desc, SQLSMALLINT RecNumber, *((void **)Value) = ptr; break; } - + if (StringLength) *StringLength = len; return ret; @@ -1463,7 +1469,7 @@ PGAPI_GetStmtAttr(HSTMT StatementHandle, case SQL_ATTR_IMP_ROW_DESC: /* 10012 */ case SQL_ATTR_IMP_PARAM_DESC: /* 10013 */ len = 4; - *((HSTMT *) Value) = descHandleFromStatementHandle(StatementHandle, Attribute); + *((HSTMT *) Value) = descHandleFromStatementHandle(StatementHandle, Attribute); break; case SQL_ATTR_CURSOR_SCROLLABLE: /* -1 */ @@ -1577,7 +1583,7 @@ PGAPI_GetDescField(SQLHDESC DescriptorHandle, DC_set_errormsg(desc, "can't SQLGetDescField for this parameter number"); break; } - } + } DC_log_error(func, "", desc); } return ret; @@ -1627,7 +1633,7 @@ PGAPI_SetDescField(SQLHDESC DescriptorHandle, break; break; } - } + } DC_log_error(func, "", desc); } return ret; @@ -1665,7 +1671,7 @@ PGAPI_SetStmtAttr(HSTMT StatementHandle, return SQL_ERROR; case SQL_ATTR_METADATA_ID: /* 10014 */ - stmt->options.metadata_id = (SQLUINTEGER) Value; + stmt->options.metadata_id = (SQLUINTEGER) Value; break; case SQL_ATTR_APP_ROW_DESC: /* 10010 */ if (SQL_NULL_HDESC == Value) @@ -1673,7 +1679,7 @@ PGAPI_SetStmtAttr(HSTMT StatementHandle, stmt->ard = &(stmt->ardi); } else - { + { stmt->ard = (ARDClass *) Value; } break; @@ -1683,7 +1689,7 @@ PGAPI_SetStmtAttr(HSTMT StatementHandle, stmt->apd = &(stmt->apdi); } else - { + { stmt->apd = (APDClass *) Value; } break; @@ -1732,7 +1738,7 @@ PGAPI_SetStmtAttr(HSTMT StatementHandle, #ifdef DRIVER_CURSOR_IMPLEMENT #define CALC_BOOKMARK_ADDR(book, offset, bind_size, index) \ (book->buffer + offset + \ - (bind_size > 0 ? bind_size : (SQL_C_VARBOOKMARK == book->returntype ? book->buflen : sizeof(UInt4))) * index) + (bind_size > 0 ? bind_size : (SQL_C_VARBOOKMARK == book->returntype ? book->buflen : sizeof(UInt4))) * index) /* SQL_NEED_DATA callback for PGAPI_BulkOperations */ typedef struct @@ -1745,7 +1751,7 @@ typedef struct int idx, processed; } bop_cdata; -static +static RETCODE bulk_ope_callback(RETCODE retcode, void *para) { RETCODE ret = retcode; @@ -1829,7 +1835,7 @@ PGAPI_BulkOperations(HSTMT hstmt, SQLSMALLINT operationX) s.operation = operationX; SC_clear_error(s.stmt); s.opts = SC_get_ARDF(s.stmt); - + s.auto_commit_needed = FALSE; if (SQL_FETCH_BY_BOOKMARK != s.operation) { @@ -1849,6 +1855,6 @@ PGAPI_BulkOperations(HSTMT hstmt, SQLSMALLINT operationX) s.need_data_callback = FALSE; ret = bulk_ope_callback(SQL_SUCCESS, &s); return ret; -} +} #endif /* DRIVER_CURSOR_IMPLEMENT */ diff --git a/pgtypes.c b/pgtypes.c index ac9641d..aa8dadf 100644 --- a/pgtypes.c +++ b/pgtypes.c @@ -20,7 +20,13 @@ #include "dlg_specific.h" #include "statement.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include "environ.h" #include "qresult.h" diff --git a/psqlodbc.c b/psqlodbc.c index b019e37..1343270 100644 --- a/psqlodbc.c +++ b/psqlodbc.c @@ -87,14 +87,16 @@ HINSTANCE NEAR s_hModule; /* Saved module handle. */ BOOL WINAPI DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) { +#ifndef USE_LIBPQ WORD wVersionRequested; WSADATA wsaData; +#endif /* USE_LIBPQ */ switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: s_hModule = hInst; /* Save for dialog boxes */ - +#ifndef USE_LIBPQ /* Load the WinSock Library */ wVersionRequested = MAKEWORD(1, 1); @@ -108,7 +110,7 @@ DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) WSACleanup(); return FALSE; } - +#endif /* USE_LIBPQ */ initialize_global_cs(); getCommonDefaults(DBMS_NAME, ODBCINST_INI, NULL); break; @@ -118,7 +120,9 @@ DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID lpReserved) case DLL_PROCESS_DETACH: finalize_global_cs(); +#ifndef USE_LIBPQ WSACleanup(); +#endif /* USE_LIBPQ*/ return TRUE; case DLL_THREAD_DETACH: diff --git a/psqlodbc.h b/psqlodbc.h index 2c57dbe..83ad5fe 100644 --- a/psqlodbc.h +++ b/psqlodbc.h @@ -5,7 +5,7 @@ * * Comments: See "notice.txt" for copyright and license information. * - * $Id: psqlodbc.h,v 1.88 2005/02/14 13:07:11 dpage Exp $ + * $Id: psqlodbc.h,v 1.89 2005/07/13 14:23:35 dpage Exp $ * */ @@ -90,9 +90,9 @@ typedef double SDOUBLE; #endif - +#ifndef USE_LIBPQ typedef UInt4 Oid; - +#endif /* USE_LIBPQ */ #ifndef FALSE #define FALSE (BOOL)0 diff --git a/psqlodbc.rc b/psqlodbc.rc index f584b3f..f26ad35 100644 --- a/psqlodbc.rc +++ b/psqlodbc.rc @@ -30,7 +30,11 @@ LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT DLG_CONFIG DIALOGEX 65, 43, 359, 219 STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +#ifdef USE_LIBPQ +CAPTION "PostgreSQL ODBC (libpq) ƒZƒbƒgƒAƒbƒv" +#else CAPTION "PostgreSQL ODBC ƒZƒbƒgƒAƒbƒv" +#endif FONT 9, "‚l‚r ƒSƒVƒbƒN", 0, 0, 0x1 BEGIN RTEXT "ƒf|ƒ^ƒ\|ƒX–¼:(&N)",IDC_DSNAMETEXT,2,9,63,17,NOT @@ -295,8 +299,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 8,0,1,2 - PRODUCTVERSION 8,0,1,2 + FILEVERSION 8,1,0,1 + PRODUCTVERSION 8,1,0,1 FILEFLAGSMASK 0x3L #ifdef _DEBUG FILEFLAGS 0x1L @@ -311,15 +315,19 @@ BEGIN BEGIN BLOCK "040904e4" BEGIN +#ifdef USE_LIBPQ + VALUE "Comments", "PostgreSQL ODBC driver (libpq based)" +#else VALUE "Comments", "PostgreSQL ODBC driver" +#endif VALUE "CompanyName", "PostgreSQL Global Development Group" VALUE "FileDescription", "PostgreSQL Driver" - VALUE "FileVersion", "08.00.0102" + VALUE "FileVersion", "08.01.0001" VALUE "InternalName", "psqlodbc" VALUE "LegalTrademarks", "ODBC(TM) is a trademark of Microsoft Corporation. Microsoft® is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation." VALUE "OriginalFilename", "psqlodbc.dll" VALUE "ProductName", "Microsoft Open Database Connectivity" - VALUE "ProductVersion", "08.00.0102" + VALUE "ProductVersion", "08.01.0001" END END BLOCK "VarFileInfo" @@ -368,7 +376,11 @@ LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US DLG_CONFIG DIALOGEX 65, 43, 305, 130 STYLE DS_SETFONT | DS_MODALFRAME | DS_3DLOOK | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU -CAPTION "PostgreSQL ODBC Driver (psqlODBC) Setup" +#ifdef USE_LIBPQ +CAPTION "PostgreSQL ODBC Driver (libpq) Setup" +#else +CAPTION "PostgreSQL ODBC Driver Setup" +#endif FONT 10, "Terminal", 0, 0, 0x0 BEGIN RTEXT "&Data Source",IDC_DSNAMETEXT,5,25,50,12,NOT WS_GROUP @@ -594,8 +606,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 8,0,1,2 - PRODUCTVERSION 8,0,1,2 + FILEVERSION 8,1,0,1 + PRODUCTVERSION 8,1,0,1 FILEFLAGSMASK 0x3L #ifdef _DEBUG FILEFLAGS 0x1L @@ -610,15 +622,19 @@ BEGIN BEGIN BLOCK "040904e4" BEGIN +#ifdef USE_LIBPQ + VALUE "Comments", "PostgreSQL ODBC driver (libpq based)" +#else VALUE "Comments", "PostgreSQL ODBC driver" +#endif VALUE "CompanyName", "PostgreSQL Global Development Group" VALUE "FileDescription", "PostgreSQL Driver" - VALUE "FileVersion", "08.00.0102" + VALUE "FileVersion", "08.01.0001" VALUE "InternalName", "psqlodbc" VALUE "LegalTrademarks", "ODBC(TM) is a trademark of Microsoft Corporation. Microsoft® is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation." VALUE "OriginalFilename", "psqlodbc.dll" VALUE "ProductName", "Microsoft Open Database Connectivity" - VALUE "ProductVersion", "08.00.0102" + VALUE "ProductVersion", "08.01.0001" END END BLOCK "VarFileInfo" diff --git a/qresult.c b/qresult.c index 72005bf..2547691 100644 --- a/qresult.c +++ b/qresult.c @@ -161,7 +161,12 @@ QR_Destructor(QResultClass *self) * If conn is defined, then we may have used "backend_tuples", so in * case we need to, free it up. Also, close the cursor. */ +#ifdef USE_LIBPQ + self->backend_tuples = NULL; + if (conn && conn->pgconn && CC_is_in_trans(conn)) +#else if (conn && conn->sock && CC_is_in_trans(conn)) +#endif /* USE_LIBPQ */ { if (!QR_close(self)) /* close the cursor if there is one */ { @@ -284,7 +289,11 @@ QR_free_memory(QResultClass *self) free(self->keyset); self->keyset = NULL; self->count_keyset_allocated = 0; +#ifdef USE_LIBPQ + if (self->reload_count > 0 && conn && conn->pgconn) +#else if (self->reload_count > 0 && conn && conn->sock) +#endif /* USE_LIBPQ */ { char plannm[32]; @@ -357,7 +366,9 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor) { if (!cursor || cursor[0] == '\0') { +#ifndef USE_LIBPQ self->status = PGRES_INTERNAL_ERROR; +#endif /* USE_LIBPQ*/ QR_set_message(self, "Internal Error -- no cursor for fetch"); return FALSE; } @@ -371,7 +382,9 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor) */ if (CI_read_fields(self->fields, self->conn)) { +#ifndef USE_LIBPQ self->status = PGRES_FIELDS_OK; +#endif /* USE_LIBPQ*/ self->num_fields = CI_get_num_fields(self->fields); if (self->haskeyset) self->num_fields -= 2; @@ -425,8 +438,11 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor) self->num_backend_rows = tuple_size + 1; self->fetch_count = tuple_size + 1; self->base = 0; - +#ifdef USE_LIBPQ + return TRUE; +#else return QR_next_tuple(self); +#endif /* USE_LIBPQ */ } else { @@ -503,6 +519,305 @@ QR_close(QResultClass *self) /* This function is called by fetch_tuples() AND SQLFetch() */ + +#ifdef USE_LIBPQ + +int +QR_next_tuple(QResultClass *self) +{ + int id; + QResultClass *res; + ConnectionClass *conn; + + /* Speed up access */ + int fetch_count = self->fetch_count; + int num_backend_rows = self->num_backend_rows; + int fetch_size, + offset = 0; + int end_tuple = self->rowset_size + self->base; + char corrected = FALSE; + TupleField *the_tuples = self->backend_tuples; + + char fetch[128]; + QueryInfo qi; + ConnInfo *ci = NULL; + if (fetch_count < fetch_count) + { + /* return a row from cache */ + mylog("next_tuple: fetch_count < fcount: returning tuple %d, fcount = %d\n", fetch_count, num_backend_rows); + self->tupleField = the_tuples + (fetch_count * self->num_fields); /* next row */ + self->fetch_count++; + return TRUE; + } + else if (self->num_backend_rows < self->cache_size) + { + /* last row from cache */ + /* We are done because we didn't even get CACHE_SIZE tuples */ + mylog("next_tuple: fcount < CACHE_SIZE: fcount = %d, fetch_count = %d\n", num_backend_rows, fetch_count); + self->tupleField = NULL; + /* self->status = PGRES_END_TUPLES;*/ + /* end of tuples */ + return -1; + } + else + { + /* + * See if we need to fetch another group of rows. We may be being + * called from send_query(), and if so, don't send another fetch, + * just fall through and read the tuples. + */ + self->tupleField = NULL; + if (!self->inTuples) + { + ci = &(self->conn->connInfo); + if (!self->cursor || !ci->drivers.use_declarefetch) + { + mylog("next_tuple: ALL_ROWS: done, fcount = %d, fetch_count = %d\n", self->num_total_rows, fetch_count); + self->tupleField = NULL; + /* self->status = PGRES_END_TUPLES;*/ + return -1; /* end of tuples */ + } + + if (self->base == num_backend_rows) + { + int row, lf; + TupleField *tuple = self->backend_tuples; + + /* not a correction */ + /* Determine the optimum cache size. */ + if (ci->drivers.fetch_max % self->rowset_size == 0) + fetch_size = ci->drivers.fetch_max; + else if (self->rowset_size < ci->drivers.fetch_max) + fetch_size = (ci->drivers.fetch_max / self->rowset_size) * self->rowset_size; + else + fetch_size = self->rowset_size; + + self->cache_size = fetch_size; + /* clear obsolete tuples */ +inolog("clear obsolete %d tuples\n", num_backend_rows); + for (row = 0; row < num_backend_rows; row++) + { + for (lf = 0; lf < self->num_fields; lf++) + { + if (tuple[lf].value != NULL) + { + free(tuple[lf].value); + tuple[lf].value = NULL; + } + } + tuple += self->num_fields; + } + self->fetch_count = 1; + } + else + { + /* need to correct */ + corrected = TRUE; + + fetch_size = end_tuple - num_backend_rows; + + self->cache_size += fetch_size; + + offset = self->fetch_count; + self->fetch_count++; + } + + if (!self->backend_tuples || self->cache_size > self->count_backend_allocated) + { + self->count_backend_allocated = 0; + if (self->num_fields > 0) + { + QR_REALLOC_return_with_error(self->backend_tuples, TupleField, + (self->num_fields * sizeof(TupleField) * self->cache_size), + self, "Out of memory while reading tuples.", FALSE) + self->count_backend_allocated = self->cache_size; + } + } + if (self->haskeyset && (!self->keyset || self->cache_size > self->count_keyset_allocated)) + { + self->count_keyset_allocated = 0; + QR_REALLOC_return_with_error(self->keyset, KeySet, + (sizeof(KeySet) * self->cache_size), + self, "Out of memory while reading tuples.", FALSE) + self->count_keyset_allocated = self->cache_size; + } + sprintf(fetch, "fetch %d in %s", fetch_size, self->cursor); + + mylog("next_tuple: sending actual fetch (%d) query '%s'\n", fetch_size, fetch); + + /* don't read ahead for the next tuple (self) ! */ + qi.row_size = self->cache_size; + qi.result_in = self; + qi.cursor = NULL; + res = CC_send_query(self->conn, fetch, &qi, CLEAR_RESULT_ON_ABORT); + if (res == NULL) + { + self->status = PGRES_FATAL_ERROR; + QR_set_message(self, "Error fetching next group."); + return FALSE; + } + self->inTuples = TRUE; + } + else + { + mylog("next_tuple: inTuples = true, falling through: fcount = %d, fetch_count = %d\n", self->num_backend_rows, self->fetch_count); + + /* + * This is a pre-fetch (fetching rows right after query but + * before any real SQLFetch() calls. This is done so the + * field attributes are available. + */ + self->fetch_count = 0; + } + } + if (!corrected) + { + self->base = 0; + self->num_backend_rows = 0; + } + + conn = self->conn; + self->tupleField = NULL; + ci = &(self->conn->connInfo); + + for (;;) + { + if (!self->cursor || !ci->drivers.use_declarefetch) + { + if (self->num_fields > 0 && + self->num_total_rows >= self->count_backend_allocated) + { + int tuple_size = self->count_backend_allocated; + mylog("REALLOC: old_count = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size); + tuple_size *= 2; + QR_REALLOC_return_with_error(self->backend_tuples, TupleField, + (tuple_size * self->num_fields * sizeof(TupleField)), + self, "Out of memory while reading tuples.", FALSE) + self->count_backend_allocated = tuple_size; + } + if (self->haskeyset && self->num_total_rows >= self->count_keyset_allocated) + { + int tuple_size = self->count_keyset_allocated; + tuple_size *= 2; + QR_REALLOC_return_with_error(self->keyset, KeySet, + (sizeof(KeySet) * tuple_size), + self, "Out of memory while reading tuples.", FALSE) + self->count_keyset_allocated = tuple_size; + } + } + id = 67; + if (!QR_read_tuple(self, (char) (id==0))) + { + self->status = PGRES_BAD_RESPONSE; + QR_set_message(self, "Error reading the tuple"); + return FALSE; + } + self->num_total_rows++; + if (self->num_fields > 0) + self->num_backend_rows++; + if (self->num_backend_rows > 0) + { + /* set to first row */ + self->tupleField = self->backend_tuples + (offset * self->num_fields); + return TRUE; + } + else + { + /* We are surely done here (we read 0 tuples) */ + mylog("_next_tuple: 'C': DONE (fcount == 0)\n"); + return -1; /* end of tuples */ + } + /* continue reading */ + } + + mylog("end of tuple list -- setting inUse to false: this = %u\n", self); + + return TRUE; +} + + +char +QR_read_tuple(QResultClass *self, char binary) +{ + Int2 field_lf; + TupleField *this_tuplefield; + KeySet *this_keyset = NULL; + Int2 bitmaplen; /* len of the bitmap in bytes */ + Int4 len; + char *buffer; + int ci_num_fields = QR_NumResultCols(self); /* speed up access */ + int num_fields = self->num_fields; /* speed up access */ + ConnectionClass *conn = self->conn; + ColumnInfoClass *flds; + int effective_cols; + char tidoidbuf[32]; + + /* set the current row to read the fields into */ + effective_cols = QR_NumPublicResultCols(self); + this_tuplefield = self->backend_tuples + (self->num_backend_rows * num_fields); + if (self->haskeyset) + { + this_keyset = self->keyset + self->num_total_rows; + this_keyset->status = 0; + } + + bitmaplen = (Int2) ci_num_fields / BYTELEN; + if ((ci_num_fields % BYTELEN) > 0) + bitmaplen++; + + /* + * At first the server sends a bitmap that indicates which database + * fields are null + */ + flds = self->fields; + for (field_lf = 0; field_lf < ci_num_fields; field_lf++) + { + /* + * NO, the field is not null. so get at first the length of + * the field (four bytes) + */ + if (!binary) + len -= VARHDRSZ; + + if (field_lf >= effective_cols) + buffer = tidoidbuf; + else + QR_MALLOC_return_with_error(buffer, char, + (len + 1), self, + PGRES_FATAL_ERROR, + "Couldn't allocate buffer", + FALSE); + if (field_lf >= effective_cols) + { + if (field_lf == effective_cols) + sscanf(buffer, "(%lu,%hu)", + &this_keyset->blocknum, &this_keyset->offset); + else + this_keyset->oid = strtoul(buffer, NULL, 10); + } + else + { + this_tuplefield[field_lf].len = len; + this_tuplefield[field_lf].value = buffer; + /* + * This can be used to set the longest length of the column + * for any row in the tuple cache. It would not be accurate + * for varchar and text fields to use this since a tuple cache + * is only 100 rows. Bpchar can be handled since the strlen of + * all rows is fixed, assuming there are not 100 nulls in a + * row! + */ + if (flds && flds->display_size && flds->display_size[field_lf] < len) + flds->display_size[field_lf] = len; + } + } + + self->currTuple++; + return TRUE; +} + +#else + int QR_next_tuple(QResultClass *self) { @@ -940,3 +1255,6 @@ QR_read_tuple(QResultClass *self, char binary) self->currTuple++; return TRUE; } + + +#endif /* USE_LIBPQ */ diff --git a/qresult.h b/qresult.h index 0a1a55f..ece650c 100644 --- a/qresult.h +++ b/qresult.h @@ -11,12 +11,18 @@ #include "psqlodbc.h" +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" #include "socket.h" +#endif /* USE_LIBPQ */ + #include "columninfo.h" #include "tuplelist.h" #include "tuple.h" +#ifndef USE_LIBPQ enum QueryResultCode_ { PGRES_EMPTY_QUERY = 0, @@ -37,7 +43,7 @@ enum QueryResultCode_ PGRES_INTERNAL_ERROR }; typedef enum QueryResultCode_ QueryResultCode; - +#endif /*USE _LIBPQ */ struct QResultClass_ { @@ -61,7 +67,11 @@ struct QResultClass_ int rowset_size; Int4 recent_processed_row_count; +#ifdef USE_LIBPQ + ExecStatusType status; +#else QueryResultCode status; +#endif /* USE_LIBPQ*/ char *message; char *cursor; /* The name of the cursor for select @@ -78,21 +88,28 @@ struct QResultClass_ char haskeyset; /* this result contains keyset ? */ KeySet *keyset; UInt4 reload_count; - UInt2 rb_alloc; /* count of allocated rollback info */ - UInt2 rb_count; /* count of rollback info */ - Rollback *rollback; - UInt2 dl_alloc; /* count of allocated deleted info */ - UInt2 dl_count; /* count of deleted info */ - UInt4 *deleted; + UInt2 rb_alloc; /* count of allocated rollback info */ + UInt2 rb_count; /* count of rollback info */ + Rollback *rollback; + UInt2 dl_alloc; /* count of allocated deleted info */ + UInt2 dl_count; /* count of deleted info */ + UInt4 *deleted; }; #define QR_get_fields(self) (self->fields) /* These functions are for retrieving data from the qresult */ +#ifdef USE_LIBPQ +/* Retrieve results from manual_tuples since it has the results */ +#define QR_get_value_manual(self, tupleno, fieldno) (TL_get_fieldval(self->manual_tuples, tupleno, fieldno)) +#define QR_get_value_backend(self,fieldno) (TL_get_fieldval(self->manual_tuples,self->currTuple, fieldno)) +#define QR_get_value_backend_row(self, tupleno, fieldno) (TL_get_fieldval(self->manual_tuples, tupleno, fieldno)) +#else #define QR_get_value_manual(self, tupleno, fieldno) (TL_get_fieldval(self->manual_tuples, tupleno, fieldno)) #define QR_get_value_backend(self, fieldno) (self->tupleField[fieldno].value) #define QR_get_value_backend_row(self, tupleno, fieldno) ((self->backend_tuples + (tupleno * self->num_fields))[fieldno].value) +#endif /* USE_LIBPQ */ /* These functions are used by both manual and backend results */ #define QR_NumResultCols(self) (CI_get_num_fields(self->fields)) diff --git a/readme.txt b/readme.txt index 33a2eb1..121138c 100644 --- a/readme.txt +++ b/readme.txt @@ -52,10 +52,23 @@ Command Line Method 3. Use NMAKE to build the dll eg: - C:\psqlodbc\> nmake /f win32.mak CFG=Release ALL - - Possible configurations are Release and Debug. - Possible build types are ALL or CLEAN. + a. Using Libpq Interface (Default): + C:\psqlodbc\> nmake /f win32.mak CFG=Release ALL PG_INC="c:\postgresql\include" + or + C:\psqlodbc\> nmake /f win32.mak CFG=Release ALL MODE=USE_LIBPQ PG_INC="c:\postgresql\include" + + Possible configurations are Release and Debug. + Possible build types are ALL or CLEAN. + PG_INC must contain the include folder name of PostgreSQL installation. + + b. Using Socket: + C:\psqlodbc\> nmake /f win32.mak CFG=Release ALL MODE=USE_SOCK + + Possible configurations are Release and Debug. + Possible build types are ALL or CLEAN. + + c. For cleaning up intermediate files: + C:\psqlodbc\> nmake /f win32.mak CFG=Release CLEAN MODE=USE_SOCK II. Using Large Objects for handling LongVarBinary (OLE Objects in Access) diff --git a/results.c b/results.c index ac1b52d..e13ee6e 100644 --- a/results.c +++ b/results.c @@ -20,7 +20,12 @@ #include #include "dlg_specific.h" #include "environ.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ #include "statement.h" #include "bind.h" #include "qresult.h" @@ -1293,13 +1298,13 @@ PGAPI_ExtendedFetch( EXTFETCH_RETURN_BOF(stmt, res) } #else - if (stmt->rowset_start < opts->rowset_size) + if (stmt->rowset_start < opts->size_of_rowset) { SC_set_error(stmt, STMT_POS_BEFORE_RECORDSET, "fetch prior and before the beggining"); stmt->rowset_start = 0; } else - stmt->rowset_start -= opts->rowset_size; + stmt->rowset_start -= opts->size_of_rowset; #endif /* DRIVER_CURSOR_IMPLEMENT */ } break; @@ -1414,7 +1419,9 @@ PGAPI_ExtendedFetch( */ if (SC_is_fetchcursor(stmt) && !stmt->manual_result) { +#ifndef USE_LIBPQ if (QR_end_tuples(res)) +#endif /* USE_LIBPQ */ return SQL_NO_DATA_FOUND; } else diff --git a/setup.c b/setup.c index 2ed8d0a..69dcff8 100644 --- a/setup.c +++ b/setup.c @@ -15,7 +15,12 @@ #include "psqlodbc.h" +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ + #include #include #include diff --git a/socket.c b/socket.c index a486228..f43db1c 100644 --- a/socket.c +++ b/socket.c @@ -12,6 +12,8 @@ *------- */ +#ifndef USE_LIBPQ + #include "socket.h" #include "connection.h" @@ -116,7 +118,7 @@ SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname ) { #if defined (POSIX_MULTITHREAD_SUPPORT) - const int bufsz = 8192; + const int bufsz = 8192; char buf[bufsz]; int error = 0; struct hostent host; @@ -128,7 +130,7 @@ SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname #ifdef HAVE_UNIX_SOCKETS struct sockaddr_un *un; #endif /* HAVE_UNIX_SOCKETS */ - int family, sLen; + int family, sLen; #ifdef WIN32 UInt4 iaddr; #else @@ -156,9 +158,9 @@ SOCK_connect_to(SocketClass *self, unsigned short port, char *hostname sLen = sizeof(self->sadr_in); if (iaddr == INADDR_NONE) { -#if defined (POSIX_MULTITHREAD_SUPPORT) +#if defined (POSIX_MULTITHREAD_SUPPORT) #if defined (HAVE_GETIPNODEBYNAME) /* Free-BSD ? */ - hp = getipnodebyname(hostname, AF_INET, 0, &error); + hp = getipnodebyname(hostname, AF_INET, 0, &error); #elif defined (PGS_REENTRANT_API_1) /* solaris, irix */ hp = gethostbyname_r(hostname, hp, buf, bufsz, &error); #elif defined (PGS_REENTRANT_API_2) /* linux */ @@ -474,3 +476,6 @@ SOCK_put_next_byte(SocketClass *self, UCHAR next_byte) } while (self->buffer_filled_out > 0); } } + + +#endif /* USE_LIBPQ */ diff --git a/statement.c b/statement.c index d90a1a9..b579fdb 100644 --- a/statement.c +++ b/statement.c @@ -15,7 +15,12 @@ #include "statement.h" #include "bind.h" + +#ifdef USE_LIBPQ +#include "libpqconnection.h" +#else #include "connection.h" +#endif /* USE_LIBPQ */ #include "qresult.h" #include "convert.h" #include "environ.h" @@ -235,7 +240,11 @@ SC_Constructor(void) rv->phstmt = NULL; rv->result = NULL; rv->curres = NULL; +#ifdef USE_LIBPQ + rv->manual_result = TRUE; +#else rv->manual_result = FALSE; +#endif /* USE_LIBPQ */ rv->prepare = FALSE; rv->prepared = FALSE; rv->status = STMT_ALLOCATED; @@ -614,7 +623,12 @@ SC_recycle_statement(StatementClass *self) * Reset only parameters that have anything to do with results */ self->status = STMT_READY; - self->manual_result = FALSE; /* very important */ +#ifdef USE_LIBPQ + self->manual_result = TRUE; /* very important */ +#else + self->manual_result = FALSE; +#endif /* USE_LIBPQ */ + self->currTuple = -1; self->rowset_start = -1; @@ -760,6 +774,19 @@ SC_create_errormsg(const StatementClass *self) else return strdup(notice); } +#ifdef USE_LIBPQ + if (conn) + { + PGconn *pgconn = conn->pgconn; + + if (!detailmsg && CC_get_errormsg(conn) && (CC_get_errormsg(conn))[0] != '\0') + { + pos = strlen(msg); + sprintf(&msg[pos], ";\n%s", CC_get_errormsg(conn)); + } + + } +#else if (conn) { SocketClass *sock = conn->sock; @@ -776,6 +803,7 @@ SC_create_errormsg(const StatementClass *self) sprintf(&msg[pos], ";\n%s", sock->errormsg); } } +#endif /* USE_LIBPQ */ return msg[0] ? strdup(msg) : NULL; } @@ -1232,7 +1260,11 @@ SC_execute(StatementClass *self) else { /* Bad Error -- The error message will be in the Connection */ +#ifdef USE_LIBPQ + if (!conn->pgconn) +#else if (!conn->sock) +#endif /* USE_LIBPQ */ SC_set_error(self, STMT_BAD_ERROR, CC_get_errormsg(conn)); else if (self->statement_type == STMT_TYPE_CREATE) { diff --git a/tuplelist.c b/tuplelist.c index 0ae2130..eaf78f0 100644 --- a/tuplelist.c +++ b/tuplelist.c @@ -51,9 +51,10 @@ TL_Destructor(TupleListClass *self) *tp; mylog("TupleList: in DESTRUCTOR\n"); - + if(self && self->list_start) + { node = self->list_start; - while (node != NULL) + while (node) { for (lf = 0; lf < self->num_fields; lf++) if (node->tuple[lf].value != NULL) @@ -63,6 +64,7 @@ TL_Destructor(TupleListClass *self) node = tp; } + } free(self); mylog("TupleList: exit DESTRUCTOR\n"); diff --git a/version.h b/version.h index 9ccdde2..e08d5b7 100644 --- a/version.h +++ b/version.h @@ -5,14 +5,14 @@ * Comments: See "notice.txt" for copyright and license information. * * Note: The version number should also be updated in : - * psqlobdc.rc, configure.ac and installer/make.bat + * psqlodbc.rc, configure.ac and installer/make.bat */ #ifndef __VERSION_H__ #define __VERSION_H__ -#define POSTGRESDRIVERVERSION "08.00.0102" -#define POSTGRES_RESOURCE_VERSION "08.00.0102\0" -#define PG_DRVFILE_VERSION 8,0,1,02 +#define POSTGRESDRIVERVERSION "08.01.0001" +#define POSTGRES_RESOURCE_VERSION "08.01.0001\0" +#define PG_DRVFILE_VERSION 8,1,0,1 #endif diff --git a/win32.mak b/win32.mak index ede22ba..1987385 100644 --- a/win32.mak +++ b/win32.mak @@ -5,9 +5,9 @@ # # Configurations: Debug, Release # Build Types: ALL, CLEAN -# Usage: NMAKE /f win32.mak CFG=[Release | Debug] [ALL | CLEAN] -# +# Usage: NMAKE /f win32.mak CFG=[Release | Debug] [ALL | CLEAN] [MODE=[USE_LIBPQ | USE_SOCK] PG_INC=] # Comments: Created by Dave Page, 2001-02-12 +# Modified by Anoop Kumar, 2005-06-27 # !MESSAGE Building the PostgreSQL Unicode 3.0 Driver for Win32... @@ -15,15 +15,33 @@ !IF "$(CFG)" == "" CFG=Release !MESSAGE No configuration specified. Defaulting to Release. -!MESSAGE !ENDIF +!IF "$(MODE)" == "" +MODE=USE_LIBPQ +!MESSAGE Using Libpq. +!ENDIF + +!IF "$(MODE)" == "USE_LIBPQ" +!IF "$(PG_INC)" == "" +PG_INC=C:\Program Files\PostgreSQL\8.0\include +!MESSAGE Using default PostgreSQL Include directory: $(PG_INC) +!ENDIF + +!IF "$(PG_LIB)" == "" +PG_LIB=C:\Program Files\PostgreSQL\8.0\lib\ms +!MESSAGE Using default PostgreSQL Library directory: $(PG_LIB) +!ENDIF +!ENDIF + +!MESSAGE + !IF "$(CFG)" != "Release" && "$(CFG)" != "Debug" !MESSAGE Invalid configuration "$(CFG)" specified. !MESSAGE You can specify a configuration when running NMAKE !MESSAGE by defining the macro CFG on the command line. For example: !MESSAGE -!MESSAGE NMAKE /f win32.mak CFG=[Release | Debug] [ALL | CLEAN] +!MESSAGE NMAKE /f win32.mak CFG=[Release | Debug] [ALL | CLEAN] [MODE=[USE_LIBPQ | USE_SOCK] PG_INC= PG_LIB=] !MESSAGE !MESSAGE Possible choices for configuration are: !MESSAGE @@ -33,6 +51,22 @@ CFG=Release !ERROR An invalid configuration was specified. !ENDIF +!IF "$(MODE)" != "USE_LIBPQ" && "$(MODE)" != "USE_SOCK" +!MESSAGE Invalid mode "$(MODE)" specified. +!MESSAGE You can specify the connection mode and PostgreSQL include folder +!MESSAGE by defining the macros MODE and PG_INC on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f win32.mak CFG=[Release | Debug] [ALL | CLEAN] [MODE=[USE_LIBPQ | USE_SOCK] PG_INC= PG_LIB=] +!MESSAGE +!MESSAGE Possible choices for mode are: +!MESSAGE +!MESSAGE "USE_LIBPQ" (Using Libpq Interface) +!MESSAGE "USE_SOCK" (Using Socket) +!MESSAGE +!ERROR An invalid mode was specified. +!ENDIF + + !IF "$(OS)" == "Windows_NT" NULL= !ELSE @@ -93,7 +127,12 @@ CLEAN : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe -CPP_PROJ=/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /D "ODBCVER=0x0300" /D "DRIVER_CURSOR_IMPLEMENT" /D "WIN_MULTITHREAD_SUPPORT" $(ADD_DEFINES) /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c + +!IF "$(MODE)"=="USE_LIBPQ" +CPP_PROJ=/nologo /MT /W3 /GX /O2 /I "$(PG_INC)" /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /D "$(MODE)" /D "ODBCVER=0x0300" /D "DRIVER_CURSOR_IMPLEMENT" /D "WIN_MULTITHREAD_SUPPORT" $(ADD_DEFINES) /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c +!ELSE +CPP_PROJ=/nologo /MT /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /D "$(MODE)" /D "ODBCVER=0x0300" /D "DRIVER_CURSOR_IMPLEMENT" /D "WIN_MULTITHREAD_SUPPORT" $(ADD_DEFINES) /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c +!ENDIF .c{$(INTDIR)}.obj:: $(CPP) @<< @@ -134,7 +173,14 @@ BSC32_FLAGS=/nologo /o"$(OUTDIR)\psqlodbc.bsc" BSC32_SBRS= \ LINK32=link.exe + +!IF "$(MODE)"=="USE_LIBPQ" +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib libpq.lib wsock32.lib /nologo /dll /incremental:no /pdb:"$(OUTDIR)\psqlodbc.pdb" /machine:I386 /def:"psqlodbc_win32.def" /out:"$(OUTDIRBIN)\psqlodbc.dll" /implib:"$(OUTDIR)\psqlodbc.lib" /libpath:"$(PG_LIB)" +!ELSE LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /incremental:no /pdb:"$(OUTDIR)\psqlodbc.pdb" /machine:I386 /def:"psqlodbc_win32.def" /out:"$(OUTDIRBIN)\psqlodbc.dll" /implib:"$(OUTDIR)\psqlodbc.lib" +!ENDIF + + DEF_FILE= "psqlodbc_win32.def" LINK32_OBJS= \ "$(INTDIR)\bind.obj" \ @@ -234,7 +280,12 @@ CLEAN : if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" CPP=cl.exe -CPP_PROJ=/nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /D "ODBCVER=0x0300" /D "DRIVER_CURSOR_IMPLEMENT" /D "WIN_MULTITHREAD_SUPPORT" $(ADD_DEFINES) /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c + +!IF "$(MODE)"=="USE_LIBPQ" +CPP_PROJ=/nologo /MTd /W3 /Gm /GX /ZI /Od /I "$(PG_INC)" /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /D "$(MODE)" /D "ODBCVER=0x0300" /D "DRIVER_CURSOR_IMPLEMENT" /D "WIN_MULTITHREAD_SUPPORT" $(ADD_DEFINES) /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c +!ELSE +CPP_PROJ=/nologo /MTd /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /D "_USRDLL" /D "PSQLODBC_EXPORTS" /D "$(MODE)" /D "ODBCVER=0x0300" /D "DRIVER_CURSOR_IMPLEMENT" /D "WIN_MULTITHREAD_SUPPORT" $(ADD_DEFINES) /Fp"$(INTDIR)\psqlodbc.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /GZ /c +!ENDIF .c{$(INTDIR)}.obj:: $(CPP) @<< @@ -275,7 +326,13 @@ BSC32_FLAGS=/nologo /o"$(OUTDIR)\psqlodbc.bsc" BSC32_SBRS= \ LINK32=link.exe + +!IF "$(MODE)"=="USE_LIBPQ" +LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib libpq.lib wsock32.lib /nologo /dll /incremental:yes /pdb:"$(OUTDIR)\psqlodbc.pdb" /debug /machine:I386 /def:"psqlodbc_win32.def" /out:"$(OUTDIR)\psqlodbc.dll" /implib:"$(OUTDIR)\psqlodbc.lib" /pdbtype:sept /libpath:"$(PG_LIB)" +!ELSE LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib wsock32.lib /nologo /dll /incremental:yes /pdb:"$(OUTDIR)\psqlodbc.pdb" /debug /machine:I386 /def:"psqlodbc_win32.def" /out:"$(OUTDIR)\psqlodbc.dll" /implib:"$(OUTDIR)\psqlodbc.lib" /pdbtype:sept +!ENDIF + DEF_FILE= "psqlodbc_win32.def" LINK32_OBJS= \ "$(INTDIR)\bind.obj" \ @@ -452,6 +509,20 @@ SOURCE=psqlodbc.c SOURCE=psqlodbc.rc +!IF "$(MODE)" == "USE_LIBPQ" + +!IF "$(CFG)" == "Release" +"$(INTDIR)\psqlodbc.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "NDEBUG" /d "MULTIBYTE" /d "USE_LIBPQ" $(SOURCE) +!ENDIF + +!IF "$(CFG)" == "Debug" +"$(INTDIR)\psqlodbc.res" : $(SOURCE) "$(INTDIR)" + $(RSC) /l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "_DEBUG" /d "USE_LIBPQ" $(SOURCE) +!ENDIF + +!ELSE + !IF "$(CFG)" == "Release" "$(INTDIR)\psqlodbc.res" : $(SOURCE) "$(INTDIR)" $(RSC) /l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "NDEBUG" /d "MULTIBYTE" $(SOURCE) @@ -462,6 +533,8 @@ SOURCE=psqlodbc.rc $(RSC) /l 0x809 /fo"$(INTDIR)\psqlodbc.res" /d "_DEBUG" $(SOURCE) !ENDIF +!ENDIF + SOURCE=qresult.c -- 2.39.5