diff options
author | Hiroshi Inoue <inoue@tpf.co.jp> | 2002-01-11 02:50:01 +0000 |
---|---|---|
committer | Hiroshi Inoue <inoue@tpf.co.jp> | 2002-01-11 02:50:01 +0000 |
commit | f43b5de649cdf8a36f7d90a96932800cb0e34162 (patch) | |
tree | 6fb1ec57cff266680ab2adaf8c0976a643627b7c /src/interfaces/odbc/windev/connection.c | |
parent | 5370cd6b03610bdb6c6dee0fbf87ad9cdf524395 (diff) |
Add a directory to save the changes until 7.3-tree is branched.
Diffstat (limited to 'src/interfaces/odbc/windev/connection.c')
-rw-r--r-- | src/interfaces/odbc/windev/connection.c | 1839 |
1 files changed, 1839 insertions, 0 deletions
diff --git a/src/interfaces/odbc/windev/connection.c b/src/interfaces/odbc/windev/connection.c new file mode 100644 index 00000000000..e057d7b73f9 --- /dev/null +++ b/src/interfaces/odbc/windev/connection.c @@ -0,0 +1,1839 @@ +/*------ + * Module: connection.c + * + * Description: This module contains routines related to + * connecting to and disconnecting from the Postgres DBMS. + * + * Classes: ConnectionClass (Functions prefix: "CC_") + * + * API functions: SQLAllocConnect, SQLConnect, SQLDisconnect, SQLFreeConnect, + * SQLBrowseConnect(NI) + * + * Comments: See "notice.txt" for copyright and license information. + *------- + */ +/* Multibyte support Eiji Tokuya 2001-03-15 */ + +#include "connection.h" + +#include <stdio.h> +#include <string.h> +#include <ctype.h> + +#include "environ.h" +#include "socket.h" +#include "statement.h" +#include "qresult.h" +#include "lobj.h" +#include "dlg_specific.h" + +#ifdef MULTIBYTE +#include "multibyte.h" +#endif + +#include "pgapifunc.h" +#include "md5.h" + +#define STMT_INCREMENT 16 /* how many statement holders to allocate + * at a time */ + +#define PRN_NULLCHECK + +extern GLOBAL_VALUES globals; + + +RETCODE SQL_API +PGAPI_AllocConnect( + HENV henv, + HDBC FAR * phdbc) +{ + EnvironmentClass *env = (EnvironmentClass *) henv; + ConnectionClass *conn; + static char *func = "PGAPI_AllocConnect"; + + mylog("%s: entering...\n", func); + + conn = CC_Constructor(); + mylog("**** %s: henv = %u, conn = %u\n", func, henv, conn); + + if (!conn) + { + env->errormsg = "Couldn't allocate memory for Connection object."; + env->errornumber = ENV_ALLOC_ERROR; + *phdbc = SQL_NULL_HDBC; + EN_log_error(func, "", env); + return SQL_ERROR; + } + + if (!EN_add_connection(env, conn)) + { + env->errormsg = "Maximum number of connections exceeded."; + env->errornumber = ENV_ALLOC_ERROR; + CC_Destructor(conn); + *phdbc = SQL_NULL_HDBC; + EN_log_error(func, "", env); + return SQL_ERROR; + } + + *phdbc = (HDBC) conn; + + return SQL_SUCCESS; +} + + +RETCODE SQL_API +PGAPI_Connect( + HDBC hdbc, + UCHAR FAR * szDSN, + SWORD cbDSN, + UCHAR FAR * szUID, + SWORD cbUID, + UCHAR FAR * szAuthStr, + SWORD cbAuthStr) +{ + ConnectionClass *conn = (ConnectionClass *) hdbc; + ConnInfo *ci; + static char *func = "PGAPI_Connect"; + + mylog("%s: entering...\n", func); + + if (!conn) + { + CC_log_error(func, "", NULL); + return SQL_INVALID_HANDLE; + } + + ci = &conn->connInfo; + + make_string(szDSN, cbDSN, ci->dsn); + + /* get the values for the DSN from the registry */ + getDSNinfo(ci, CONN_OVERWRITE); + logs_on_off(1, ci->drivers.debug, ci->drivers.commlog); + /* initialize pg_version from connInfo.protocol */ + CC_initialize_pg_version(conn); + + /* + * override values from DSN info with UID and authStr(pwd) This only + * occurs if the values are actually there. + */ + make_string(szUID, cbUID, ci->username); + make_string(szAuthStr, cbAuthStr, ci->password); + + /* fill in any defaults */ + getDSNdefaults(ci); + + qlog("conn = %u, %s(DSN='%s', UID='%s', PWD='%s')\n", conn, func, ci->dsn, ci->username, ci->password); + + if (CC_connect(conn, FALSE) <= 0) + { + /* Error messages are filled in */ + CC_log_error(func, "Error on CC_connect", conn); + return SQL_ERROR; + } + + mylog("%s: returning...\n", func); + + return SQL_SUCCESS; +} + + +RETCODE SQL_API +PGAPI_BrowseConnect( + HDBC hdbc, + UCHAR FAR * szConnStrIn, + SWORD cbConnStrIn, + UCHAR FAR * szConnStrOut, + SWORD cbConnStrOutMax, + SWORD FAR * pcbConnStrOut) +{ + static char *func = "PGAPI_BrowseConnect"; + + mylog("%s: entering...\n", func); + + return SQL_SUCCESS; +} + + +/* Drop any hstmts open on hdbc and disconnect from database */ +RETCODE SQL_API +PGAPI_Disconnect( + HDBC hdbc) +{ + ConnectionClass *conn = (ConnectionClass *) hdbc; + static char *func = "PGAPI_Disconnect"; + + + mylog("%s: entering...\n", func); + + if (!conn) + { + CC_log_error(func, "", NULL); + return SQL_INVALID_HANDLE; + } + + qlog("conn=%u, %s\n", conn, func); + + if (conn->status == CONN_EXECUTING) + { + conn->errornumber = CONN_IN_USE; + conn->errormsg = "A transaction is currently being executed"; + CC_log_error(func, "", conn); + return SQL_ERROR; + } + + logs_on_off(-1, conn->connInfo.drivers.debug, conn->connInfo.drivers.commlog); + mylog("%s: about to CC_cleanup\n", func); + + /* Close the connection and free statements */ + CC_cleanup(conn); + + mylog("%s: done CC_cleanup\n", func); + mylog("%s: returning...\n", func); + + return SQL_SUCCESS; +} + + +RETCODE SQL_API +PGAPI_FreeConnect( + HDBC hdbc) +{ + ConnectionClass *conn = (ConnectionClass *) hdbc; + static char *func = "PGAPI_FreeConnect"; + + mylog("%s: entering...\n", func); + mylog("**** in %s: hdbc=%u\n", func, hdbc); + + if (!conn) + { + CC_log_error(func, "", NULL); + return SQL_INVALID_HANDLE; + } + + /* Remove the connection from the environment */ + if (!EN_remove_connection(conn->henv, conn)) + { + conn->errornumber = CONN_IN_USE; + conn->errormsg = "A transaction is currently being executed"; + CC_log_error(func, "", conn); + return SQL_ERROR; + } + + CC_Destructor(conn); + + mylog("%s: returning...\n", func); + + return SQL_SUCCESS; +} + + +/* + * 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->errormsg = NULL; + rv->errornumber = 0; + rv->errormsg_created = FALSE; + + rv->status = CONN_NOT_CONNECTED; + rv->transact_status = CONN_IN_AUTOCOMMIT; /* autocommit by default */ + + memset(&rv->connInfo, 0, sizeof(ConnInfo)); +#ifdef DRIVER_CURSOR_IMPLEMENT + rv->connInfo.updatable_cursors = 1; +#endif /* DRIVER_CURSOR_IMPLEMENT */ + memcpy(&(rv->connInfo.drivers), &globals, sizeof(globals)); + 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->lobj_type = PG_TYPE_LO; + + 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; +#ifdef MULTIBYTE + rv->client_encoding = NULL; + rv->server_encoding = NULL; +#endif /* MULTIBYTE */ + + + /* Initialize statement options to defaults */ + /* Statements under this conn will inherit these options */ + + InitializeStatementOptions(&rv->stmtOptions); + + + } + 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"); + +#ifdef MULTIBYTE + if (self->client_encoding) + free(self->client_encoding); + if (self->server_encoding) + free(self->server_encoding); +#endif /* MULTIBYTE */ + /* Free up statement holders */ + if (self->stmts) + { + free(self->stmts); + self->stmts = NULL; + } + mylog("after free statement holders\n"); + + /* Free cached table info */ + if (self->col_info) + { + int i; + + 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); + + free(self->col_info[i]); + } + free(self->col_info); + } + + + free(self); + + mylog("exit CC_Destructor\n"); + + return 1; +} + + +/* Return how many cursors are opened on this connection */ +int +CC_cursor_count(ConnectionClass *self) +{ + StatementClass *stmt; + int i, + count = 0; + + mylog("CC_cursor_count: self=%u, num_stmts=%d\n", self, self->num_stmts); + + for (i = 0; i < self->num_stmts; i++) + { + stmt = self->stmts[i]; + if (stmt && stmt->result && stmt->result->cursor) + count++; + } + + mylog("CC_cursor_count: returning %d\n", count); + + return count; +} + + +void +CC_clear_error(ConnectionClass *self) +{ + self->errornumber = 0; + self->errormsg = NULL; + self->errormsg_created = FALSE; +} + + +/* + * Used to cancel a transaction. + * We are almost always in the middle of a transaction. + */ +char +CC_abort(ConnectionClass *self) +{ + QResultClass *res; + + if (CC_is_in_trans(self)) + { + res = NULL; + + mylog("CC_abort: sending ABORT!\n"); + + res = CC_send_query(self, "ABORT", NULL); + CC_set_no_trans(self); + + if (res != NULL) + QR_Destructor(res); + else + return FALSE; + + } + + return TRUE; +} + + +/* This is called by SQLDisconnect also */ +char +CC_cleanup(ConnectionClass *self) +{ + int i; + StatementClass *stmt; + + 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 */ + if (self->sock) + { + 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; + } + } + + /* Check for translation dll */ +#ifdef WIN32 + if (self->translation_handle) + { + FreeLibrary(self->translation_handle); + self->translation_handle = NULL; + } +#endif + + mylog("exit CC_Cleanup\n"); + return TRUE; +} + + +int +CC_set_translation(ConnectionClass *self) +{ + +#ifdef WIN32 + + if (self->translation_handle != NULL) + { + FreeLibrary(self->translation_handle); + self->translation_handle = NULL; + } + + if (self->connInfo.translation_dll[0] == 0) + return TRUE; + + self->translation_option = atoi(self->connInfo.translation_option); + self->translation_handle = LoadLibrary(self->connInfo.translation_dll); + + if (self->translation_handle == NULL) + { + self->errornumber = CONN_UNABLE_TO_LOAD_DLL; + self->errormsg = "Could not load the translation DLL."; + return FALSE; + } + + self->DataSourceToDriver + = (DataSourceToDriverProc) GetProcAddress(self->translation_handle, + "SQLDataSourceToDriver"); + + self->DriverToDataSource + = (DriverToDataSourceProc) GetProcAddress(self->translation_handle, + "SQLDriverToDataSource"); + + if (self->DataSourceToDriver == NULL || self->DriverToDataSource == NULL) + { + self->errornumber = CONN_UNABLE_TO_LOAD_DLL; + self->errormsg = "Could not find translation DLL functions."; + return FALSE; + } +#endif + 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; + +mylog("MD5 user=%s password=%s\n", ci->username, ci->password); + 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 do_password) +{ + 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]; + static char *func = "CC_connect"; + +#ifdef MULTIBYTE + char *encoding; +#endif /* MULTIBYTE */ + + mylog("%s: entering...\n", func); + + if (do_password) + + 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\n", + ci->drivers.text_as_longvarchar, + ci->drivers.unknowns_as_longvarchar, + ci->drivers.bools_as_char); + +#ifdef MULTIBYTE + 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); + } + qlog(" extra_systable_prefixes='%s', conn_settings='%s' conn_encoding='%s'\n", + ci->drivers.extra_systable_prefixes, + ci->drivers.conn_settings, + encoding ? encoding : ""); +#else + qlog(" extra_systable_prefixes='%s', conn_settings='%s'\n", + ci->drivers.extra_systable_prefixes, + ci->drivers.conn_settings); +#endif + + if (self->status != CONN_NOT_CONNECTED) + { + self->errormsg = "Already connected."; + self->errornumber = CONN_OPENDB_ERROR; + return 0; + } + + if (ci->server[0] == '\0' || ci->port[0] == '\0' || ci->database[0] == '\0') + { + self->errornumber = CONN_INIREAD_ERROR; + self->errormsg = "Missing server name, port, or database name in call to CC_connect."; + return 0; + } + + 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); + +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) + { + self->errornumber = CONNECTION_SERVER_NOT_REACHED; + self->errormsg = "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); + if (SOCK_get_errcode(sock) != 0) + { + mylog("connection to the server socket failed.\n"); + self->errornumber = CONNECTION_SERVER_NOT_REACHED; + self->errormsg = "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, NAMEDATALEN); + 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"); + self->errornumber = CONN_INVALID_AUTHENTICATION; + self->errormsg = "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 (do_password) + 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); + self->errornumber = CONN_INVALID_AUTHENTICATION; + self->errormsg = msgbuffer; + qlog("ERROR from backend during authentication: '%s'\n", self->errormsg); + 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 (do_password) + { + mylog("in 'R' do_password\n"); + areq = AUTH_REQ_PASSWORD; + do_password = FALSE; + } + else + { + + areq = SOCK_get_int(sock, 4); + if (areq == AUTH_REQ_MD5) + SOCK_get_n_char(sock, salt, 4); + 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: + self->errormsg = "Kerberos 4 authentication not supported"; + self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED; + return 0; + + case AUTH_REQ_KRB5: + self->errormsg = "Kerberos 5 authentication not supported"; + self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED; + return 0; + + case AUTH_REQ_PASSWORD: + mylog("in AUTH_REQ_PASSWORD\n"); + + if (ci->password[0] == '\0') + { + self->errornumber = CONNECTION_NEED_PASSWORD; + self->errormsg = "A password is required for this connection."; + return -1; /* 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: + self->errormsg = "Password crypt authentication not supported"; + self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED; + return 0; + case AUTH_REQ_MD5: + mylog("in AUTH_REQ_MD5\n"); + if (ci->password[0] == '\0') + { + self->errornumber = CONNECTION_NEED_PASSWORD; + self->errormsg = "A password is required for this connection."; + return -1; /* need password */ + } + if (md5_auth_send(self, salt)) + { + self->errormsg = "md5 hashing failed"; + self->errornumber = CONN_INVALID_AUTHENTICATION; + return 0; + } + break; + + case AUTH_REQ_SCM_CREDS: + self->errormsg = "Unix socket credential authentication not supported"; + self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED; + return 0; + + default: + self->errormsg = "Unknown authentication type"; + self->errornumber = CONN_AUTH_TYPE_UNSUPPORTED; + return 0; + } + break; + case 'K': /* Secret key (6.4 protocol) */ + (void) SOCK_get_int(sock, 4); /* pid */ + (void) SOCK_get_int(sock, 4); /* key */ + + break; + case 'Z': /* Backend is ready for new query (6.4) */ + ReadyForQuery = TRUE; + break; + default: + self->errormsg = "Unexpected protocol character during authentication"; + self->errornumber = CONN_INVALID_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); + if (res == NULL || QR_get_status(res) != PGRES_EMPTY_QUERY) + { + mylog("got no result from the empty query. (probably database does not exist)\n"); + self->errornumber = CONNECTION_NO_SUCH_DATABASE; + self->errormsg = "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 + */ + + /* + * 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_lookup_lo(self); /* a hack to get the oid of our large + * object oid type */ + CC_lookup_pg_version(self); /* Get PostgreSQL version for SQLGetInfo + * use */ + + 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++) + { + if (!self->stmts[i]) + { + stmt->hdbc = self; + self->stmts[i] = stmt; + return TRUE; + } + } + + /* 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; + + return TRUE; +} + + +char +CC_remove_statement(ConnectionClass *self, StatementClass *stmt) +{ + int i; + + for (i = 0; i < self->num_stmts; i++) + { + if (self->stmts[i] == stmt && stmt->status != STMT_EXECUTING) + { + self->stmts[i] = NULL; + return TRUE; + } + } + + return FALSE; +} + + +/* + * 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; + static char msg[4096]; + + mylog("enter CC_create_errormsg\n"); + + msg[0] = '\0'; + + if (self->errormsg) + strcpy(msg, self->errormsg); + + 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; +} + + +char +CC_get_error(ConnectionClass *self, int *number, char **message) +{ + int rv; + + mylog("enter CC_get_error\n"); + + /* Create a very informative errormsg if it hasn't been done yet. */ + if (!self->errormsg_created) + { + self->errormsg = CC_create_errormsg(self); + self->errormsg_created = TRUE; + } + + if (self->errornumber) + { + *number = self->errornumber; + *message = self->errormsg; + } + rv = (self->errornumber != 0); + + self->errornumber = 0; /* clear the error */ + + mylog("exit CC_get_error\n"); + + return rv; +} + + +/* + * 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) +{ + QResultClass *result_in = NULL, + *res = NULL, + *retres = NULL; + char swallow, + *wq; + int id; + SocketClass *sock = self->sock; + int maxlen, + empty_reqs; + BOOL msg_truncated, + ReadyToReturn, + tuples_return = FALSE, + query_completed = FALSE, + before_64 = PG_VERSION_LT(self, 6.4); + + /* ERROR_MSG_LENGTH is suffcient */ + static 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); + + /* 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) + { + self->errornumber = CONNECTION_MSG_TOO_LONG; + self->errormsg = "Query string is too long"; + return NULL; + } + + if ((NULL == query) || (query[0] == '\0')) + return NULL; + + if (SOCK_get_errcode(sock) != 0) + { + self->errornumber = CONNECTION_COULD_NOT_SEND; + self->errormsg = "Could not send Query to backend"; + CC_set_no_trans(self); + return NULL; + } + + SOCK_put_char(sock, 'Q'); + if (SOCK_get_errcode(sock) != 0) + { + self->errornumber = CONNECTION_COULD_NOT_SEND; + self->errormsg = "Could not send Query to backend"; + CC_set_no_trans(self); + return NULL; + } + + SOCK_put_string(sock, query); + SOCK_flush_output(sock); + + if (SOCK_get_errcode(sock) != 0) + { + self->errornumber = CONNECTION_COULD_NOT_SEND; + self->errormsg = "Could not send Query to backend"; + CC_set_no_trans(self); + return NULL; + } + + mylog("send_query: done sending query\n"); + + ReadyToReturn = FALSE; + empty_reqs = 0; + for (wq = query; isspace((unsigned char) *wq); wq++) + ; + if (*wq == '\0') + empty_reqs = 1; + while (!ReadyToReturn) + { + /* what type of message is coming now ? */ + id = SOCK_get_char(sock); + + if ((SOCK_get_errcode(sock) != 0) || (id == EOF)) + { + self->errornumber = CONNECTION_NO_RESPONSE; + self->errormsg = "No response from the backend"; + + mylog("send_query: 'id' - %s\n", self->errormsg); + CC_set_no_trans(self); + 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) + { + self->errornumber = CONNECTION_NO_RESPONSE; + self->errormsg = "No response from backend while receiving a portal query command"; + mylog("send_query: 'C' - %s\n", self->errormsg); + CC_set_no_trans(self); + ReadyToReturn = TRUE; + retres = NULL; + } + else + { + mylog("send_query: ok - 'C' - %s\n", cmdbuffer); + + if (res == NULL) /* allow for "show" style notices */ + res = QR_Constructor(); + + mylog("send_query: setting cmdbuffer = '%s'\n", cmdbuffer); + + /* Only save the first command */ + 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 (res && QR_get_aborted(res)) + retres = res; + else if (tuples_return) + retres = result_in; + else if (query_completed) + retres = res; + else + ReadyToReturn = FALSE; + } + break; + case 'N': /* NOTICE: */ + msg_truncated = SOCK_get_string(sock, cmdbuffer, ERROR_MSG_LENGTH); + if (!res) + res = QR_Constructor(); + 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 (!res) + res = QR_Constructor(); + if ((swallow != '\0') || SOCK_get_errcode(sock) != 0) + { + self->errornumber = CONNECTION_BACKEND_CRAZY; + self->errormsg = "Unexpected protocol character from backend (send_query - I)"; + QR_set_status(res, PGRES_FATAL_ERROR); + ReadyToReturn = TRUE; + retres = res; + 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'; + + self->errormsg = msgbuffer; + + mylog("send_query: 'E' - %s\n", self->errormsg); + qlog("ERROR from backend during send_query: '%s'\n", self->errormsg); + + /* We should report that an error occured. Zoltan */ + if (!res) + res = QR_Constructor(); + + if (!strncmp(self->errormsg, "FATAL", 5)) + { + self->errornumber = CONNECTION_SERVER_REPORTED_ERROR; + CC_set_no_trans(self); + } + else + self->errornumber = CONNECTION_SERVER_REPORTED_WARNING; + QR_set_status(res, PGRES_FATAL_ERROR); + QR_set_aborted(res, 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 */ + result_in = qi ? qi->result_in : NULL; + + if (result_in == NULL) + { + result_in = QR_Constructor(); + mylog("send_query: 'T' no result_in: res = %u\n", result_in); + if (!result_in) + { + self->errornumber = CONNECTION_COULD_NOT_RECEIVE; + self->errormsg = "Could not create result info in send_query."; + ReadyToReturn = TRUE; + retres = NULL; + break; + } + + if (qi) + QR_set_cache_size(result_in, qi->row_size); + + if (!QR_fetch_tuples(result_in, self, qi ? qi->cursor : NULL)) + { + self->errornumber = CONNECTION_COULD_NOT_RECEIVE; + self->errormsg = QR_get_message(result_in); + ReadyToReturn = TRUE; + retres = NULL; + break; + } + } + else + { /* next fetch, so reuse an existing result */ + + /* + * called from QR_next_tuple and must return + * immediately. + */ + ReadyToReturn = TRUE; + if (!QR_fetch_tuples(result_in, NULL, NULL)) + { + self->errornumber = CONNECTION_COULD_NOT_RECEIVE; + self->errormsg = QR_get_message(result_in); + retres = NULL; + break; + } + retres = result_in; + } + + tuples_return = TRUE; + break; + case 'D': /* Copy in command began successfully */ + if (!res) + res = QR_Constructor(); + if (QR_command_successful(res)) + QR_set_status(res, PGRES_COPY_IN); + ReadyToReturn = TRUE; + retres = res; + break; + case 'B': /* Copy out command began successfully */ + if (!res) + res = QR_Constructor(); + if (QR_command_successful(res)) + QR_set_status(res, PGRES_COPY_OUT); + ReadyToReturn = TRUE; + retres = res; + break; + default: + self->errornumber = CONNECTION_BACKEND_CRAZY; + self->errormsg = "Unexpected protocol character from backend (send_query)"; + CC_set_no_trans(self); + + mylog("send_query: error - %s\n", self->errormsg); + ReadyToReturn = TRUE; + retres = NULL; + break; + } + + /* + * There were no ReadyForQuery response before 6.4. + */ + if (before_64) + { + if (empty_reqs == 0 && (query_completed || tuples_return)) + break; + } + } + + /* + * Break before being ready to return. + */ + if (!ReadyToReturn) + { + if (res && QR_get_aborted(res)) + retres = res; + else if (tuples_return) + retres = result_in; + else + retres = res; + } + + /* + * set notice message to result_in. + */ + if (result_in && res && retres == result_in) + { + if (QR_command_successful(result_in)) + QR_set_status(result_in, QR_get_status(res)); + QR_set_notice(result_in, QR_get_notice(res)); + } + + /* + * Cleanup garbage results before returning. + */ + if (res && retres != res) + QR_Destructor(res); + if (result_in && retres != result_in) + { + if (qi && qi->result_in) + ; + else + QR_Destructor(result_in); + } + 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 */ + static 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 (SOCK_get_errcode(sock) != 0) + { + self->errornumber = CONNECTION_COULD_NOT_SEND; + self->errormsg = "Could not send function to backend"; + CC_set_no_trans(self); + return FALSE; + } + + SOCK_put_string(sock, "F "); + if (SOCK_get_errcode(sock) != 0) + { + self->errornumber = CONNECTION_COULD_NOT_SEND; + self->errormsg = "Could not send function to backend"; + CC_set_no_trans(self); + 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); + self->errormsg = msgbuffer; + + mylog("send_function(V): 'E' - %s\n", self->errormsg); + qlog("ERROR from backend during send_function: '%s'\n", self->errormsg); + + return FALSE; + + case 'Z': + break; + + default: + self->errornumber = CONNECTION_BACKEND_CRAZY; + self->errormsg = "Unexpected protocol character from backend (send_function, args)"; + CC_set_no_trans(self); + + mylog("send_function: error - %s\n", self->errormsg); + 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); + self->errormsg = msgbuffer; + + mylog("send_function(G): 'E' - %s\n", self->errormsg); + qlog("ERROR from backend during send_function: '%s'\n", self->errormsg); + + 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: + self->errornumber = CONNECTION_BACKEND_CRAZY; + self->errormsg = "Unexpected protocol character from backend (send_function, result)"; + CC_set_no_trans(self); + + mylog("send_function: error - %s\n", self->errormsg); + 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; + static char *func = "CC_send_settings"; + + + 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 FALSE; + stmt = (StatementClass *) hstmt; + + stmt->internal = TRUE; /* ensure no BEGIN/COMMIT/ABORT stuff */ + + /* Set the Datestyle to the format the driver expects it to be in */ + result = PGAPI_ExecDirect(hstmt, "set DateStyle to 'ISO'", SQL_NTS); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + status = FALSE; + + 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); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + status = FALSE; + + mylog("%s: result %d, status %d from set geqo\n", func, result, status); + + } + + /* KSQO */ + if (ci->drivers.ksqo) + { + result = PGAPI_ExecDirect(hstmt, "set ksqo to 'ON'", SQL_NTS); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + status = FALSE; + + mylog("%s: result %d, status %d from set ksqo\n", func, result, status); + + } + + /* Global settings */ + if (ci->drivers.conn_settings[0] != '\0') + { + cs = strdup(ci->drivers.conn_settings); + ptr = strtok(cs, ";"); + while (ptr) + { + result = PGAPI_ExecDirect(hstmt, ptr, SQL_NTS); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + status = FALSE; + + mylog("%s: result %d, status %d from '%s'\n", func, result, status, ptr); + + ptr = strtok(NULL, ";"); + } + + free(cs); + } + + /* Per Datasource settings */ + if (ci->conn_settings[0] != '\0') + { + cs = strdup(ci->conn_settings); + ptr = strtok(cs, ";"); + while (ptr) + { + result = PGAPI_ExecDirect(hstmt, ptr, SQL_NTS); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + status = FALSE; + + mylog("%s: result %d, status %d from '%s'\n", func, result, status, ptr); + + ptr = strtok(NULL, ";"); + } + + free(cs); + } + + + PGAPI_FreeStmt(hstmt, SQL_DROP); + + return status; +} + + +/* + * 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; + static char *func = "CC_lookup_lo"; + + 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); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + { + PGAPI_FreeStmt(hstmt, SQL_DROP); + return; + } + + result = PGAPI_Fetch(hstmt); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + { + PGAPI_FreeStmt(hstmt, SQL_DROP); + return; + } + + 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)) + { + PGAPI_FreeStmt(hstmt, SQL_DROP); + return; + } + + mylog("Got the large object oid: %d\n", self->lobj_type); + qlog(" [ Large Object oid = %d ]\n", self->lobj_type); + + result = PGAPI_FreeStmt(hstmt, SQL_DROP); +} + + +/* + * 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)) + { + 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; + } +} + + +/* + * 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) +{ + HSTMT hstmt; + StatementClass *stmt; + RETCODE result; + char szVersion[32]; + int major, + minor; + static char *func = "CC_lookup_pg_version"; + + 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; + + /* get the server's version if possible */ + result = PGAPI_ExecDirect(hstmt, "select version()", SQL_NTS); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + { + PGAPI_FreeStmt(hstmt, SQL_DROP); + return; + } + + result = PGAPI_Fetch(hstmt); + if ((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) + { + PGAPI_FreeStmt(hstmt, SQL_DROP); + return; + } + + 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; + } + + /* + * 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); + + 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); + + result = PGAPI_FreeStmt(hstmt, SQL_DROP); +} + + +void +CC_log_error(char *func, char *desc, 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->errornumber, nullcheck(self->errormsg)); + mylog("CONN ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, nullcheck(self->errormsg)); + 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); +#undef PRN_NULLCHECK +} + +int +CC_get_max_query_len(const ConnectionClass *conn) +{ + int value; + + /* 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; +} |