Hiroshi Inoue 6852741c18 [2002-03-28]
1) Prepare to separate 4 kinds of Descriptor handles.
2) Detect the transaction status more naturally.
3) Improve Parse Statement functionality for the use
   of updatable cursors.
4) Improve updatable cursors.
5) Implement SQLGetDescField() and improve SQLColAttribute().
6) etc.
2002-03-28 08:08:07 +00:00

647 lines
15 KiB
C

/*-------
* Module: environ.c
*
* Description: This module contains routines related to
* the environment, such as storing connection handles,
* and returning errors.
*
* Classes: EnvironmentClass (Functions prefix: "EN_")
*
* API functions: SQLAllocEnv, SQLFreeEnv, SQLError
*
* Comments: See "notice.txt" for copyright and license information.
*-------
*/
#include "environ.h"
#include "connection.h"
#include "dlg_specific.h"
#include "statement.h"
#include <stdlib.h>
#include <string.h>
#include "pgapifunc.h"
extern GLOBAL_VALUES globals;
/* The one instance of the handles */
ConnectionClass *conns[MAX_CONNECTIONS];
RETCODE SQL_API
PGAPI_AllocEnv(HENV FAR * phenv)
{
static char *func = "PGAPI_AllocEnv";
mylog("**** in PGAPI_AllocEnv ** \n");
/*
* Hack for systems on which none of the constructor-making techniques
* in psqlodbc.c work: if globals appears not to have been
* initialized, then cause it to be initialized. Since this should be
* the first function called in this shared library, doing it here
* should work.
*/
if (globals.socket_buffersize <= 0)
getCommonDefaults(DBMS_NAME, ODBCINST_INI, NULL);
*phenv = (HENV) EN_Constructor();
if (!*phenv)
{
*phenv = SQL_NULL_HENV;
EN_log_error(func, "Error allocating environment", NULL);
return SQL_ERROR;
}
mylog("** exit PGAPI_AllocEnv: phenv = %u **\n", *phenv);
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_FreeEnv(HENV henv)
{
static char *func = "PGAPI_FreeEnv";
EnvironmentClass *env = (EnvironmentClass *) henv;
mylog("**** in PGAPI_FreeEnv: env = %u ** \n", env);
if (env && EN_Destructor(env))
{
mylog(" ok\n");
return SQL_SUCCESS;
}
mylog(" error\n");
EN_log_error(func, "Error freeing environment", env);
return SQL_ERROR;
}
#define DRVMNGRDIV 511
/* Returns the next SQL error information. */
RETCODE SQL_API
PGAPI_StmtError( HSTMT hstmt,
SWORD RecNumber,
UCHAR FAR * szSqlState,
SDWORD FAR * pfNativeError,
UCHAR FAR * szErrorMsg,
SWORD cbErrorMsgMax,
SWORD FAR * pcbErrorMsg,
UWORD flag)
{
/* CC: return an error of a hstmt */
StatementClass *stmt = (StatementClass *) hstmt;
char *msg;
int status;
BOOL partial_ok = ((flag & PODBC_ALLOW_PARTIAL_EXTRACT) != 0),
clear_str = ((flag & PODBC_ERROR_CLEAR) != 0);
SWORD msglen, stapos, wrtlen, pcblen;
mylog("**** PGAPI_StmtError: hstmt=%u <%d>\n", hstmt, cbErrorMsgMax);
if (cbErrorMsgMax < 0)
return SQL_ERROR;
if (!SC_get_error(stmt, &status, &msg) || NULL == msg || !msg[0])
{
mylog("SC_Get_error returned nothing.\n");
if (NULL != szSqlState)
strcpy(szSqlState, "00000");
if (NULL != pcbErrorMsg)
*pcbErrorMsg = 0;
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
szErrorMsg[0] = '\0';
return SQL_NO_DATA_FOUND;
}
mylog("SC_get_error: status = %d, msg = #%s#\n", status, msg);
msglen = (SWORD) strlen(msg);
/*
* Even though an application specifies a larger error message
* buffer, the driver manager changes it silently.
* Therefore we divide the error message into ...
*/
if (stmt->error_recsize < 0)
{
if (cbErrorMsgMax > 0)
stmt->error_recsize = cbErrorMsgMax - 1; /* apply the first request */
else
stmt->error_recsize = DRVMNGRDIV;
}
if (RecNumber < 0)
{
if (0 == stmt->errorpos)
RecNumber = 1;
else
RecNumber = 2 + (stmt->errorpos - 1) / stmt->error_recsize;
}
stapos = (RecNumber - 1) * stmt->error_recsize;
if (stapos > msglen)
return SQL_NO_DATA_FOUND;
pcblen = wrtlen = msglen - stapos;
if (pcblen > stmt->error_recsize)
pcblen = stmt->error_recsize;
if (0 == cbErrorMsgMax)
wrtlen = 0;
else if (wrtlen >= cbErrorMsgMax)
{
if (partial_ok)
wrtlen = cbErrorMsgMax - 1;
else if (cbErrorMsgMax <= stmt->error_recsize)
wrtlen = 0;
else
wrtlen = stmt->error_recsize;
}
if (wrtlen > pcblen)
wrtlen = pcblen;
if (NULL != pcbErrorMsg)
*pcbErrorMsg = pcblen;
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
{
memcpy(szErrorMsg, msg + stapos, wrtlen);
szErrorMsg[wrtlen] = '\0';
}
if (NULL != pfNativeError)
*pfNativeError = status;
if (NULL != szSqlState)
switch (status)
{
/* now determine the SQLSTATE to be returned */
case STMT_ROW_VERSION_CHANGED:
strcpy(szSqlState, "01001");
/* data truncated */
break;
case STMT_TRUNCATED:
strcpy(szSqlState, "01004");
/* data truncated */
break;
case STMT_INFO_ONLY:
strcpy(szSqlState, "00000");
/* just information that is returned, no error */
break;
case STMT_BAD_ERROR:
strcpy(szSqlState, "08S01");
/* communication link failure */
break;
case STMT_CREATE_TABLE_ERROR:
strcpy(szSqlState, "S0001");
/* table already exists */
break;
case STMT_STATUS_ERROR:
case STMT_SEQUENCE_ERROR:
strcpy(szSqlState, "S1010");
/* Function sequence error */
break;
case STMT_NO_MEMORY_ERROR:
strcpy(szSqlState, "S1001");
/* memory allocation failure */
break;
case STMT_COLNUM_ERROR:
strcpy(szSqlState, "S1002");
/* invalid column number */
break;
case STMT_NO_STMTSTRING:
strcpy(szSqlState, "S1001");
/* having no stmtstring is also a malloc problem */
break;
case STMT_ERROR_TAKEN_FROM_BACKEND:
strcpy(szSqlState, "S1000");
/* general error */
break;
case STMT_INTERNAL_ERROR:
strcpy(szSqlState, "S1000");
/* general error */
break;
case STMT_ROW_OUT_OF_RANGE:
strcpy(szSqlState, "S1107");
break;
case STMT_OPERATION_CANCELLED:
strcpy(szSqlState, "S1008");
break;
case STMT_NOT_IMPLEMENTED_ERROR:
strcpy(szSqlState, "S1C00"); /* == 'driver not
* capable' */
break;
case STMT_OPTION_OUT_OF_RANGE_ERROR:
strcpy(szSqlState, "S1092");
break;
case STMT_BAD_PARAMETER_NUMBER_ERROR:
strcpy(szSqlState, "S1093");
break;
case STMT_INVALID_COLUMN_NUMBER_ERROR:
strcpy(szSqlState, "S1002");
break;
case STMT_RESTRICTED_DATA_TYPE_ERROR:
strcpy(szSqlState, "07006");
break;
case STMT_INVALID_CURSOR_STATE_ERROR:
strcpy(szSqlState, "24000");
break;
case STMT_ERROR_IN_ROW:
strcpy(szSqlState, "01S01");
break;
case STMT_OPTION_VALUE_CHANGED:
strcpy(szSqlState, "01S02");
break;
case STMT_POS_BEFORE_RECORDSET:
strcpy(szSqlState, "01S06");
break;
case STMT_INVALID_CURSOR_NAME:
strcpy(szSqlState, "34000");
break;
case STMT_NO_CURSOR_NAME:
strcpy(szSqlState, "S1015");
break;
case STMT_INVALID_ARGUMENT_NO:
strcpy(szSqlState, "S1009");
/* invalid argument value */
break;
case STMT_INVALID_CURSOR_POSITION:
strcpy(szSqlState, "S1109");
break;
case STMT_RETURN_NULL_WITHOUT_INDICATOR:
strcpy(szSqlState, "22002");
break;
case STMT_VALUE_OUT_OF_RANGE:
strcpy(szSqlState, "22003");
break;
case STMT_OPERATION_INVALID:
strcpy(szSqlState, "S1011");
break;
case STMT_INVALID_DESCRIPTOR_IDENTIFIER:
strcpy(szSqlState, "HY091");
break;
case STMT_INVALID_OPTION_IDENTIFIER:
strcpy(szSqlState, "HY092");
break;
case STMT_EXEC_ERROR:
default:
strcpy(szSqlState, "S1000");
/* also a general error */
break;
}
mylog(" szSqlState = '%s',len=%d, szError='%s'\n", szSqlState, pcblen, szErrorMsg);
if (clear_str)
{
stmt->errorpos = stapos + wrtlen;
if (stmt->errorpos >= msglen)
SC_clear_error(stmt);
}
if (wrtlen == 0)
return SQL_SUCCESS_WITH_INFO;
else
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_ConnectError( HDBC hdbc,
SWORD RecNumber,
UCHAR FAR * szSqlState,
SDWORD FAR * pfNativeError,
UCHAR FAR * szErrorMsg,
SWORD cbErrorMsgMax,
SWORD FAR * pcbErrorMsg,
UWORD flag)
{
ConnectionClass *conn = (ConnectionClass *) hdbc;
char *msg;
int status;
BOOL once_again = FALSE;
SWORD msglen;
mylog("**** PGAPI_ConnectError: hdbc=%u <%d>\n", hdbc, cbErrorMsgMax);
if (RecNumber != 1)
return SQL_NO_DATA_FOUND;
if (cbErrorMsgMax < 0)
return SQL_ERROR;
if (!CC_get_error(conn, &status, &msg) || NULL == msg)
{
mylog("CC_Get_error returned nothing.\n");
if (NULL != szSqlState)
strcpy(szSqlState, "00000");
if (NULL != pcbErrorMsg)
*pcbErrorMsg = 0;
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
szErrorMsg[0] = '\0';
return SQL_NO_DATA_FOUND;
}
mylog("CC_get_error: status = %d, msg = #%s#\n", status, msg);
msglen = strlen(msg);
if (NULL != pcbErrorMsg)
{
*pcbErrorMsg = msglen;
if (cbErrorMsgMax == 0)
once_again = TRUE;
else if (msglen >= cbErrorMsgMax)
*pcbErrorMsg = cbErrorMsgMax - 1;
}
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
strncpy_null(szErrorMsg, msg, cbErrorMsgMax);
if (NULL != pfNativeError)
*pfNativeError = status;
if (NULL != szSqlState)
switch (status)
{
case STMT_OPTION_VALUE_CHANGED:
case CONN_OPTION_VALUE_CHANGED:
strcpy(szSqlState, "01S02");
break;
case STMT_TRUNCATED:
case CONN_TRUNCATED:
strcpy(szSqlState, "01004");
/* data truncated */
break;
case CONN_INIREAD_ERROR:
strcpy(szSqlState, "IM002");
/* data source not found */
break;
case CONNECTION_SERVER_NOT_REACHED:
case CONN_OPENDB_ERROR:
strcpy(szSqlState, "08001");
/* unable to connect to data source */
break;
case CONN_INVALID_AUTHENTICATION:
case CONN_AUTH_TYPE_UNSUPPORTED:
strcpy(szSqlState, "28000");
break;
case CONN_STMT_ALLOC_ERROR:
strcpy(szSqlState, "S1001");
/* memory allocation failure */
break;
case CONN_IN_USE:
strcpy(szSqlState, "S1000");
/* general error */
break;
case CONN_UNSUPPORTED_OPTION:
strcpy(szSqlState, "IM001");
/* driver does not support this function */
case CONN_INVALID_ARGUMENT_NO:
strcpy(szSqlState, "S1009");
/* invalid argument value */
break;
case CONN_TRANSACT_IN_PROGRES:
strcpy(szSqlState, "S1010");
/*
* when the user tries to switch commit mode in a
* transaction
*/
/* -> function sequence error */
break;
case CONN_NO_MEMORY_ERROR:
strcpy(szSqlState, "S1001");
break;
case CONN_NOT_IMPLEMENTED_ERROR:
case STMT_NOT_IMPLEMENTED_ERROR:
strcpy(szSqlState, "S1C00");
break;
case STMT_RETURN_NULL_WITHOUT_INDICATOR:
strcpy(szSqlState, "22002");
break;
case CONN_VALUE_OUT_OF_RANGE:
case STMT_VALUE_OUT_OF_RANGE:
strcpy(szSqlState, "22003");
break;
default:
strcpy(szSqlState, "S1000");
/* general error */
break;
}
mylog(" szSqlState = '%s',len=%d, szError='%s'\n", szSqlState, msglen, szErrorMsg);
if (once_again)
{
conn->errornumber = status;
return SQL_SUCCESS_WITH_INFO;
}
else
return SQL_SUCCESS;
}
RETCODE SQL_API
PGAPI_EnvError( HENV henv,
SWORD RecNumber,
UCHAR FAR * szSqlState,
SDWORD FAR * pfNativeError,
UCHAR FAR * szErrorMsg,
SWORD cbErrorMsgMax,
SWORD FAR * pcbErrorMsg,
UWORD flag)
{
EnvironmentClass *env = (EnvironmentClass *) henv;
char *msg;
int status;
mylog("**** PGAPI_EnvError: henv=%u <%d>\n", henv, cbErrorMsgMax);
if (RecNumber != 1)
return SQL_NO_DATA_FOUND;
if (cbErrorMsgMax < 0)
return SQL_ERROR;
if (!EN_get_error(env, &status, &msg) || NULL == msg)
{
mylog("EN_get_error: status = %d, msg = #%s#\n", status, msg);
if (NULL != szSqlState)
strcpy(szSqlState, "00000");
if (NULL != pcbErrorMsg)
*pcbErrorMsg = 0;
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
szErrorMsg[0] = '\0';
return SQL_NO_DATA_FOUND;
}
mylog("EN_get_error: status = %d, msg = #%s#\n", status, msg);
if (NULL != pcbErrorMsg)
*pcbErrorMsg = (SWORD) strlen(msg);
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
strncpy_null(szErrorMsg, msg, cbErrorMsgMax);
if (NULL != pfNativeError)
*pfNativeError = status;
if (szSqlState)
{
switch (status)
{
case ENV_ALLOC_ERROR:
/* memory allocation failure */
strcpy(szSqlState, "S1001");
break;
default:
strcpy(szSqlState, "S1000");
/* general error */
break;
}
}
return SQL_SUCCESS;
}
/* Returns the next SQL error information. */
RETCODE SQL_API
PGAPI_Error(
HENV henv,
HDBC hdbc,
HSTMT hstmt,
UCHAR FAR * szSqlState,
SDWORD FAR * pfNativeError,
UCHAR FAR * szErrorMsg,
SWORD cbErrorMsgMax,
SWORD FAR * pcbErrorMsg)
{
RETCODE ret;
UWORD flag = PODBC_ALLOW_PARTIAL_EXTRACT | PODBC_ERROR_CLEAR;
mylog("**** PGAPI_Error: henv=%u, hdbc=%u hstmt=%d\n", henv, hdbc, hstmt);
if (cbErrorMsgMax < 0)
return SQL_ERROR;
if (SQL_NULL_HSTMT != hstmt)
ret = PGAPI_StmtError(hstmt, -1, szSqlState, pfNativeError,
szErrorMsg, cbErrorMsgMax, pcbErrorMsg, flag);
else if (SQL_NULL_HDBC != hdbc)
ret = PGAPI_ConnectError(hdbc, -1, szSqlState, pfNativeError,
szErrorMsg, cbErrorMsgMax, pcbErrorMsg, flag);
else if (SQL_NULL_HENV != hdbc)
ret = PGAPI_EnvError(henv, -1, szSqlState, pfNativeError,
szErrorMsg, cbErrorMsgMax, pcbErrorMsg, flag);
else
{
if (NULL != szSqlState)
strcpy(szSqlState, "00000");
if (NULL != pcbErrorMsg)
*pcbErrorMsg = 0;
if ((NULL != szErrorMsg) && (cbErrorMsgMax > 0))
szErrorMsg[0] = '\0';
ret = SQL_NO_DATA_FOUND;
}
mylog("**** PGAPI_Error exit code=%d\n", ret);
return ret;
}
/*
* EnvironmentClass implementation
*/
EnvironmentClass *
EN_Constructor(void)
{
EnvironmentClass *rv;
rv = (EnvironmentClass *) malloc(sizeof(EnvironmentClass));
if (rv)
{
rv->errormsg = 0;
rv->errornumber = 0;
rv->flag = 0;
}
return rv;
}
char
EN_Destructor(EnvironmentClass *self)
{
int lf;
char rv = 1;
mylog("in EN_Destructor, self=%u\n", self);
/*
* the error messages are static strings distributed throughout the
* source--they should not be freed
*/
/* Free any connections belonging to this environment */
for (lf = 0; lf < MAX_CONNECTIONS; lf++)
{
if (conns[lf] && conns[lf]->henv == self)
rv = rv && CC_Destructor(conns[lf]);
}
free(self);
mylog("exit EN_Destructor: rv = %d\n", rv);
#ifdef _MEMORY_DEBUG_
debug_memory_check();
#endif /* _MEMORY_DEBUG_ */
return rv;
}
char
EN_get_error(EnvironmentClass *self, int *number, char **message)
{
if (self && self->errormsg && self->errornumber)
{
*message = self->errormsg;
*number = self->errornumber;
self->errormsg = 0;
self->errornumber = 0;
return 1;
}
else
return 0;
}
char
EN_add_connection(EnvironmentClass *self, ConnectionClass *conn)
{
int i;
mylog("EN_add_connection: self = %u, conn = %u\n", self, conn);
for (i = 0; i < MAX_CONNECTIONS; i++)
{
if (!conns[i])
{
conn->henv = self;
conns[i] = conn;
mylog(" added at i =%d, conn->henv = %u, conns[i]->henv = %u\n", i, conn->henv, conns[i]->henv);
return TRUE;
}
}
return FALSE;
}
char
EN_remove_connection(EnvironmentClass *self, ConnectionClass *conn)
{
int i;
for (i = 0; i < MAX_CONNECTIONS; i++)
if (conns[i] == conn && conns[i]->status != CONN_EXECUTING)
{
conns[i] = NULL;
return TRUE;
}
return FALSE;
}
void
EN_log_error(char *func, char *desc, EnvironmentClass *self)
{
if (self)
qlog("ENVIRON ERROR: func=%s, desc='%s', errnum=%d, errmsg='%s'\n", func, desc, self->errornumber, self->errormsg);
else
qlog("INVALID ENVIRON HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
}