Commit 6d7735e7 authored by Byron Nikolaidis's avatar Byron Nikolaidis

Update for version 6-40-0002 and re-merge Thomas' changes.

parent d73cf044
......@@ -128,12 +128,13 @@ static char *func="SQLBindParameter";
stmt->parameters[ipar].EXEC_buffer = NULL;
}
if (pcbValue && *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET)
/* Data at exec macro only valid for C char/binary data */
if ((fSqlType == SQL_LONGVARBINARY || fSqlType == SQL_LONGVARCHAR) && pcbValue && *pcbValue <= SQL_LEN_DATA_AT_EXEC_OFFSET)
stmt->parameters[ipar].data_at_exec = TRUE;
else
stmt->parameters[ipar].data_at_exec = FALSE;
mylog("SQLBindParamater: ipar = %d, *pcbValue = %d, data_at_exec = %d\n", ipar, pcbValue ? *pcbValue: -777, stmt->parameters[ipar].data_at_exec);
mylog("SQLBindParamater: ipar=%d, paramType=%d, fCType=%d, fSqlType=%d, cbColDef=%d, ibScale=%d, rgbValue=%d, *pcbValue = %d, data_at_exec = %d\n", ipar, fParamType, fCType, fSqlType, cbColDef, ibScale, rgbValue, pcbValue ? *pcbValue: -777, stmt->parameters[ipar].data_at_exec);
return SQL_SUCCESS;
}
......@@ -195,6 +196,9 @@ mylog("**** SQLBindCol: stmt = %u, icol = %d\n", stmt, icol);
icol--; /* use zero based col numbers from here out */
/* Reset for SQLGetData */
stmt->bindings[icol].data_left = -1;
if (rgbValue == NULL) {
/* we have to unbind the column */
stmt->bindings[icol].buflen = 0;
......@@ -357,6 +361,7 @@ int i;
new_bindings[i].buflen = 0;
new_bindings[i].buffer = NULL;
new_bindings[i].used = NULL;
new_bindings[i].data_left = -1;
}
return new_bindings;
......
......@@ -17,6 +17,7 @@
*/
struct BindInfoClass_ {
Int4 buflen; /* size of buffer */
Int4 data_left; /* amount of data left to read (SQLGetData) */
char *buffer; /* pointer to the buffer */
Int4 *used; /* used space in the buffer (for strings not counting the '\0') */
Int2 returntype; /* kind of conversion to be applied when returning (SQL_C_DEFAULT, SQL_C_CHAR...) */
......
......@@ -31,7 +31,6 @@
extern GLOBAL_VALUES globals;
// void CC_test(ConnectionClass *self);
RETCODE SQL_API SQLAllocConnect(
HENV henv,
......@@ -252,6 +251,14 @@ ConnectionClass *rv;
rv->translation_handle = NULL;
rv->DataSourceToDriver = NULL;
rv->DriverToDataSource = NULL;
/* Initialize statement options to defaults */
/* Statements under this conn will inherit these options */
InitializeStatementOptions(&rv->stmtOptions);
}
return rv;
}
......@@ -337,7 +344,7 @@ QResultClass *res;
mylog("CC_abort: sending ABORT!\n");
res = CC_send_query(self, "ABORT", NULL, NULL);
res = CC_send_query(self, "ABORT", NULL);
CC_set_no_trans(self);
if (res != NULL)
......@@ -664,7 +671,7 @@ static char *func="CC_connect";
/* database really exists on the server machine */
mylog("sending an empty query...\n");
res = CC_send_query(self, " ", NULL, NULL);
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;
......@@ -691,8 +698,6 @@ static char *func="CC_connect";
CC_send_settings(self);
CC_lookup_lo(self); /* a hack to get the oid of our large object oid type */
// CC_test(self);
CC_clear_error(self); /* clear any initial command errors */
self->status = CONN_CONNECTED;
......@@ -812,9 +817,9 @@ int rv;
'declare cursor C3326857 for ...' and 'fetch 100 in C3326857' statements.
*/
QResultClass *
CC_send_query(ConnectionClass *self, char *query, QResultClass *result_in, char *cursor)
CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi)
{
QResultClass *res = NULL;
QResultClass *result_in, *res = NULL;
char id, swallow;
SocketClass *sock = self->sock;
static char msgbuffer[MAX_MESSAGE_LEN+1];
......@@ -970,7 +975,7 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
swallow = SOCK_get_char(sock);
if ((swallow != '\0') || SOCK_get_errcode(sock) != 0) {
self->errornumber = CONNECTION_BACKEND_CRAZY;
self->errormsg = "Unexpected protocol character from backend";
self->errormsg = "Unexpected protocol character from backend (send_query - I)";
res = QR_Constructor();
QR_set_status(res, PGRES_FATAL_ERROR);
return res;
......@@ -1006,7 +1011,9 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
SOCK_get_string(sock, msgbuffer, MAX_MESSAGE_LEN);
break;
case 'T': /* Tuple results start here */
if (result_in == NULL) {
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) {
......@@ -1015,7 +1022,10 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
return NULL;
}
if ( ! QR_fetch_tuples(result_in, self, cursor)) {
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);
return NULL;
......@@ -1040,7 +1050,7 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
return res;
default:
self->errornumber = CONNECTION_BACKEND_CRAZY;
self->errormsg = "Unexpected protocol character from backend";
self->errormsg = "Unexpected protocol character from backend (send_query)";
CC_set_no_trans(self);
mylog("send_query: error - %s\n", self->errormsg);
......@@ -1058,7 +1068,6 @@ static char msgbuffer[MAX_MESSAGE_LEN+1];
int i;
mylog("send_function(): conn=%u, fnid=%d, result_is_int=%d, nargs=%d\n", self, fnid, result_is_int, nargs);
// qlog("conn=%u, func=%d\n", self, fnid);
if (SOCK_get_errcode(sock) != 0) {
self->errornumber = CONNECTION_COULD_NOT_SEND;
......@@ -1124,9 +1133,12 @@ int i;
return FALSE;
case 'Z':
break;
default:
self->errornumber = CONNECTION_BACKEND_CRAZY;
self->errormsg = "Unexpected protocol character from backend";
self->errormsg = "Unexpected protocol character from backend (send_function, args)";
CC_set_no_trans(self);
mylog("send_function: error - %s\n", self->errormsg);
......@@ -1178,7 +1190,7 @@ int i;
default:
self->errornumber = CONNECTION_BACKEND_CRAZY;
self->errormsg = "Unexpected protocol character from backend";
self->errormsg = "Unexpected protocol character from backend (send_function, result)";
CC_set_no_trans(self);
mylog("send_function: error - %s\n", self->errormsg);
......@@ -1352,107 +1364,3 @@ CC_log_error(char *func, char *desc, ConnectionClass *self)
qlog("INVALID CONNECTION HANDLE ERROR: func=%s, desc='%s'\n", func, desc);
}
/*
void
CC_test(ConnectionClass *self)
{
static char *func = "CC_test";
HSTMT hstmt1;
RETCODE result;
char pktab[255], fktab[255], pkcol[255], fkcol[255], tgname[255];
SDWORD pktab_len, pkcol_len, fktab_len, fkcol_len, ur_len, dr_len, tgname_len;
SWORD cols, seq;
SDWORD update_rule, delete_rule;
mylog( "%s: entering...\n", func);
result = SQLAllocStmt( self, &hstmt1);
if((result != SQL_SUCCESS) && (result != SQL_SUCCESS_WITH_INFO)) {
return;
}
result = SQLPrimaryKeys(hstmt1, NULL, 0, NULL, 0, "t1", SQL_NTS);
qlog("SQLPrimaryKeys result = %d\n", result);
result = SQLNumResultCols(hstmt1, &cols);
qlog("cols SQLTables result = %d\n", result);
result = SQLBindCol(hstmt1, 3, SQL_C_CHAR, pktab, sizeof(pktab), &pktab_len);
qlog("bind result = %d\n", result);
result = SQLBindCol(hstmt1, 4, SQL_C_CHAR, pkcol, sizeof(pkcol), &pkcol_len);
qlog("bind result = %d\n", result);
result = SQLBindCol(hstmt1, 5, SQL_C_SHORT, &seq, 0, NULL);
qlog("bind result = %d\n", result);
result = SQLFetch(hstmt1);
qlog("SQLFetch result = %d\n", result);
while (result != SQL_NO_DATA_FOUND) {
qlog("fetch on stmt1: result = %d, pktab='%s', pkcol='%s', seq=%d\n",
result, pktab, pkcol, seq);
result = SQLFetch(hstmt1);
}
qlog("SQLFetch result = %d\n", result);
// Test of case #1
result = SQLForeignKeys(hstmt1, "", SQL_NTS, "", SQL_NTS, "t1", SQL_NTS,
NULL, 0, NULL, 0, NULL, 0);
// Test of case #2
result = SQLForeignKeys(hstmt1, "", SQL_NTS, "", SQL_NTS, NULL, 0,
NULL, 0, NULL, 0, "ar_register", SQL_NTS);
// Test of case #3
result = SQLForeignKeys(hstmt1, NULL, 0, NULL, 0, "employee", SQL_NTS,
NULL, 0, NULL, 0, "invoice", SQL_NTS);
qlog("SQLForeignKeys result = %d\n", result);
result = SQLNumResultCols(hstmt1, &cols);
qlog("cols SQLTables result = %d\n", result);
result = SQLBindCol(hstmt1, 3, SQL_C_CHAR, pktab, sizeof(pktab), &pktab_len);
qlog("bind result = %d\n", result);
result = SQLBindCol(hstmt1, 4, SQL_C_CHAR, pkcol, sizeof(pkcol), &pkcol_len);
qlog("bind result = %d\n", result);
result = SQLBindCol(hstmt1, 7, SQL_C_CHAR, fktab, sizeof(fktab), &fktab_len);
qlog("bind result = %d\n", result);
result = SQLBindCol(hstmt1, 8, SQL_C_CHAR, fkcol, sizeof(fkcol), &fkcol_len);
qlog("bind result = %d\n", result);
result = SQLBindCol(hstmt1, 9, SQL_C_SHORT, &seq, 0, NULL);
qlog("bind result = %d\n", result);
result = SQLBindCol(hstmt1, 10, SQL_C_LONG, &update_rule, 0, &ur_len);
qlog("bind result = %d\n", result);
result = SQLBindCol(hstmt1, 11, SQL_C_LONG, &delete_rule, 0, &dr_len);
qlog("bind result = %d\n", result);
result = SQLBindCol(hstmt1, 14, SQL_C_CHAR, tgname, sizeof(tgname), &tgname_len);
qlog("bind result = %d\n", result);
result = SQLFetch(hstmt1);
qlog("SQLFetch result = %d\n", result);
while (result != SQL_NO_DATA_FOUND) {
qlog("fetch on stmt1: result = %d, pktab='%s', pkcol='%s', fktab='%s', fkcol='%s', seq=%d, update_rule=%d, ur_len=%d, delete_rule=%d, dr_len=%d, tgname='%s', tgname_len=%d\n",
result, pktab, pkcol, fktab, fkcol, seq, update_rule, ur_len, delete_rule, dr_len, tgname, tgname_len);
result = SQLFetch(hstmt1);
}
qlog("SQLFetch result = %d\n", result);
SQLFreeStmt(hstmt1, SQL_DROP);
}
*/
......@@ -35,33 +35,34 @@ typedef enum {
} CONN_Status;
/* These errors have general sql error state */
#define CONNECTION_SERVER_NOT_REACHED 1
#define CONNECTION_MSG_TOO_LONG 3
#define CONNECTION_COULD_NOT_SEND 4
#define CONNECTION_NO_SUCH_DATABASE 5
#define CONNECTION_BACKEND_CRAZY 6
#define CONNECTION_NO_RESPONSE 7
#define CONNECTION_SERVER_REPORTED_ERROR 8
#define CONNECTION_COULD_NOT_RECEIVE 9
#define CONNECTION_SERVER_REPORTED_WARNING 10
#define CONNECTION_NEED_PASSWORD 12
#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
/* These errors correspond to specific SQL states */
#define CONN_INIREAD_ERROR 1
#define CONN_OPENDB_ERROR 2
#define CONN_STMT_ALLOC_ERROR 3
#define CONN_IN_USE 4
#define CONN_UNSUPPORTED_OPTION 5
#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 6
#define CONN_INVALID_ARGUMENT_NO 206
/* SetConnectOption: corresponds to ODBC--"S1009" */
#define CONN_TRANSACT_IN_PROGRES 7
#define CONN_NO_MEMORY_ERROR 8
#define CONN_NOT_IMPLEMENTED_ERROR 9
#define CONN_INVALID_AUTHENTICATION 10
#define CONN_AUTH_TYPE_UNSUPPORTED 11
#define CONN_UNABLE_TO_LOAD_DLL 12
#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
/* Conn_status defines */
#define CONN_IN_AUTOCOMMIT 0x01
......@@ -200,6 +201,7 @@ typedef BOOL (FAR WINAPI *DriverToDataSourceProc) (UDWORD,
/******* The Connection handle ************/
struct ConnectionClass_ {
HENV henv; /* environment this connection was created on */
StatementOptions stmtOptions;
char *errormsg;
int errornumber;
CONN_Status status;
......@@ -244,7 +246,7 @@ char CC_connect(ConnectionClass *self, char do_password);
char CC_add_statement(ConnectionClass *self, StatementClass *stmt);
char CC_remove_statement(ConnectionClass *self, StatementClass *stmt);
char CC_get_error(ConnectionClass *self, int *number, char **message);
QResultClass *CC_send_query(ConnectionClass *self, char *query, QResultClass *result_in, char *cursor);
QResultClass *CC_send_query(ConnectionClass *self, char *query, QueryInfo *qi);
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);
......
This diff is collapsed.
......@@ -31,13 +31,13 @@ typedef struct {
int copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *value, int col);
int copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType,
PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue, char multiple);
PTR rgbValue, SDWORD cbValueMax, SDWORD *pcbValue);
int copy_statement_with_parameters(StatementClass *stmt);
char *convert_escape(char *value);
char *convert_money(char *s);
char parse_datetime(char *buf, SIMPLE_TIME *st);
char *convert_linefeeds(char *s, char *dst, size_t max);
int convert_linefeeds(char *s, char *dst, size_t max);
char *convert_special_chars(char *si, char *dst, int used);
int convert_pgbinary_to_char(char *value, char *rgbValue, int cbValueMax);
......@@ -46,6 +46,6 @@ int convert_to_pgbinary(unsigned char *in, char *out, int len);
void encode(char *in, char *out);
void decode(char *in, char *out);
int convert_lo(StatementClass *stmt, void *value, Int2 fCType, PTR rgbValue,
SDWORD cbValueMax, SDWORD *pcbValue, char multiple);
SDWORD cbValueMax, SDWORD *pcbValue);
#endif
......@@ -112,6 +112,8 @@ int CALLBACK driver_optionsProc(HWND hdlg,
CheckDlgButton(hdlg, DRV_PARSE, globals.parse);
CheckDlgButton(hdlg, DRV_CANCELASFREESTMT, globals.cancel_as_freestmt);
SetDlgItemInt(hdlg, DRV_CACHE_SIZE, globals.fetch_max, FALSE);
SetDlgItemInt(hdlg, DRV_VARCHAR_SIZE, globals.max_varchar_size, FALSE);
SetDlgItemInt(hdlg, DRV_LONGVARCHAR_SIZE, globals.max_longvarchar_size, TRUE);
......@@ -150,6 +152,8 @@ int CALLBACK driver_optionsProc(HWND hdlg,
globals.parse = IsDlgButtonChecked(hdlg, DRV_PARSE);
globals.cancel_as_freestmt = IsDlgButtonChecked(hdlg, DRV_CANCELASFREESTMT);
globals.fetch_max = GetDlgItemInt(hdlg, DRV_CACHE_SIZE, NULL, FALSE);
globals.max_varchar_size = GetDlgItemInt(hdlg, DRV_VARCHAR_SIZE, NULL, FALSE);
globals.max_longvarchar_size= GetDlgItemInt(hdlg, DRV_LONGVARCHAR_SIZE, NULL, TRUE); // allows for SQL_NO_TOTAL
......@@ -176,6 +180,7 @@ int CALLBACK driver_optionsProc(HWND hdlg,
CheckDlgButton(hdlg, DRV_USEDECLAREFETCH, DEFAULT_USEDECLAREFETCH);
CheckDlgButton(hdlg, DRV_PARSE, DEFAULT_PARSE);
CheckDlgButton(hdlg, DRV_CANCELASFREESTMT, DEFAULT_CANCELASFREESTMT);
/* Unknown Sizes */
CheckDlgButton(hdlg, DRV_UNKNOWN_DONTKNOW, 0);
......@@ -682,6 +687,14 @@ char temp[256];
else if ( ! override)
globals.parse = DEFAULT_PARSE;
// SQLCancel calls SQLFreeStmt in Driver Manager
SQLGetPrivateProfileString(section, INI_CANCELASFREESTMT, "",
temp, sizeof(temp), filename);
if ( temp[0] )
globals.cancel_as_freestmt = atoi(temp);
else if ( ! override)
globals.cancel_as_freestmt = DEFAULT_CANCELASFREESTMT;
// Readonly is stored in the driver section AND per datasource
SQLGetPrivateProfileString(section, INI_READONLY, "",
temp, sizeof(temp), filename);
......@@ -818,6 +831,10 @@ char tmp[128];
SQLWritePrivateProfileString(DBMS_NAME,
INI_PARSE, tmp, ODBCINST_INI);
sprintf(tmp, "%d", globals.cancel_as_freestmt);
SQLWritePrivateProfileString(DBMS_NAME,
INI_CANCELASFREESTMT, tmp, ODBCINST_INI);
sprintf(tmp, "%d", globals.max_varchar_size);
SQLWritePrivateProfileString(DBMS_NAME,
INI_MAXVARCHARSIZE, tmp, ODBCINST_INI);
......
......@@ -64,6 +64,8 @@
#define INI_UNIQUEINDEX "UniqueIndex" /* Recognize unique indexes */
#define INI_UNKNOWNSIZES "UnknownSizes" /* How to handle unknown result set sizes */
#define INI_CANCELASFREESTMT "CancelAsFreeStmt"
#define INI_USEDECLAREFETCH "UseDeclareFetch" /* Use Declare/Fetch cursors */
/* More ini stuff */
......@@ -108,6 +110,8 @@
#define DEFAULT_LIE 0
#define DEFAULT_PARSE 0
#define DEFAULT_CANCELASFREESTMT 0
#define DEFAULT_EXTRASYSTABLEPREFIXES "dd_;"
/* prototypes */
......
......@@ -144,6 +144,14 @@ int status;
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;
......@@ -171,7 +179,15 @@ int status;
case STMT_NO_CURSOR_NAME:
strcpy(szSqlState, "S1015");
break;
default:
case STMT_INVALID_ARGUMENT_NO:
strcpy(szSqlState, "S1009");
// invalid argument value
break;
case STMT_INVALID_CURSOR_POSITION:
strcpy(szSqlState, "S1109");
break;
default:
strcpy(szSqlState, "S1000");
// also a general error
break;
......@@ -218,6 +234,10 @@ int status;
if (NULL != szSqlState)
switch(status) {
case STMT_OPTION_VALUE_CHANGED:
case CONN_OPTION_VALUE_CHANGED:
strcpy(szSqlState, "01S02");
break;
case CONN_INIREAD_ERROR:
strcpy(szSqlState, "IM002");
// data source not found
......@@ -254,6 +274,7 @@ int status;
strcpy(szSqlState, "S1001");
break;
case CONN_NOT_IMPLEMENTED_ERROR:
case STMT_NOT_IMPLEMENTED_ERROR:
strcpy(szSqlState, "S1C00");
break;
default:
......
......@@ -36,6 +36,8 @@
#include "bind.h"
#include "lobj.h"
extern GLOBAL_VALUES globals;
// Perform a Prepare on the SQL statement
RETCODE SQL_API SQLPrepare(HSTMT hstmt,
......@@ -342,7 +344,7 @@ int lf;
mylog("SQLTransact: sending on conn %d '%s'\n", conn, stmt_string);
res = CC_send_query(conn, stmt_string, NULL, NULL);
res = CC_send_query(conn, stmt_string, NULL);
CC_set_no_trans(conn);
if ( ! res) {
......@@ -364,12 +366,14 @@ int lf;
// - - - - - - - - -
RETCODE SQL_API SQLCancel(
HSTMT hstmt) // Statement to cancel.
{
static char *func="SQLCancel";
StatementClass *stmt = (StatementClass *) hstmt;
RETCODE result;
HMODULE hmodule;
FARPROC addr;
mylog( "%s: entering...\n", func);
......@@ -380,8 +384,35 @@ StatementClass *stmt = (StatementClass *) hstmt;
}
// Not in the middle of SQLParamData/SQLPutData so cancel like a close.
if (stmt->data_at_exec < 0)
return SQLFreeStmt(hstmt, SQL_CLOSE);
if (stmt->data_at_exec < 0) {
/* MAJOR HACK for Windows to reset the driver manager's cursor state:
Because of what seems like a bug in the Odbc driver manager,
SQLCancel does not act like a SQLFreeStmt(CLOSE), as many
applications depend on this behavior. So, this
brute force method calls the driver manager's function on
behalf of the application.
*/
#ifdef WIN32
if (globals.cancel_as_freestmt) {
hmodule = GetModuleHandle("ODBC32");
addr = GetProcAddress(hmodule, "SQLFreeStmt");
result = addr( (char *) (stmt->phstmt) - 96, SQL_CLOSE);
}
else {
result = SQLFreeStmt( hstmt, SQL_CLOSE);
}
#else
result = SQLFreeStmt( hstmt, SQL_CLOSE);
#endif
mylog("SQLCancel: SQLFreeStmt returned %d\n", result);
SC_clear_error(hstmt);
return SQL_SUCCESS;
}
// In the middle of SQLParamData/SQLPutData, so cancel that.
// Note, any previous data-at-exec buffers will be freed in the recycle
......
This diff is collapsed.
......@@ -44,13 +44,13 @@ generate_filename(char* dirname,char* prefix,char* filename)
return;
strcpy(filename,dirname);
strcat(filename,DIRSEPERATOR);
strcat(filename,DIRSEPARATOR);
if(prefix != 0)
strcat(filename,prefix);
#ifndef WIN32
strcat(filename,ptr->pw_name);
#endif
sprintf(filename,"%s%d%s",filename,pid,".log");
sprintf(filename,"%s%u%s",filename,pid,".log");
return;
}
......
......@@ -39,33 +39,41 @@
#ifdef MY_LOG
#define MYLOGFILE "mylog_"
#ifndef WIN32
#define MYLOGDIR "/tmp"
#else
#define MYLOGDIR "c:"
#endif
void mylog(); /* prototype */
#define MYLOGFILE "mylog_"
#ifndef WIN32
#define MYLOGDIR "/tmp"
#else
#define MYLOGDIR "c:"
#endif
void mylog(); /* prototype */
#else
#define mylog // mylog
#ifndef WIN32
#define mylog(args...) /* GNU convention for variable arguments */
#else
#define mylog // mylog
#endif
#endif
#ifdef Q_LOG
#define QLOGFILE "psqlodbc_"
#ifndef WIN32
#define QLOGDIR "/tmp"
#else
#define QLOGDIR "c:"
#endif
void qlog(); /* prototype */
#define QLOGFILE "psqlodbc_"
#ifndef WIN32
#define QLOGDIR "/tmp"
#else
#define QLOGDIR "c:"
#endif
void qlog(); /* prototype */
#else
#define qlog // qlog
#ifndef WIN32
#define qlog(args...) /* GNU convention for variable arguments */
#else
#define qlog // qlog
#endif
#endif
#ifndef WIN32
#define DIRSEPERATOR "/"
#define DIRSEPARATOR "/"
#else
#define DIRSEPERATOR "\\"
#define DIRSEPARATOR "\\"
#endif
void remove_newlines(char *string);
......
This diff is collapsed.
......@@ -73,7 +73,104 @@ Int4 pgtypes_defined[] = {
PG_TYPE_BYTEA,
PG_TYPE_LO,
0 };
/* These are the SQL Types reported in SQLGetTypeInfo. */
Int2 sqlTypes [] = {
SQL_BIGINT,
/* SQL_BINARY, */
SQL_BIT,
SQL_CHAR,
SQL_DATE,
SQL_DECIMAL,
SQL_DOUBLE,
SQL_FLOAT,
SQL_INTEGER,
SQL_LONGVARBINARY,
SQL_LONGVARCHAR,
SQL_NUMERIC,
SQL_REAL,
SQL_SMALLINT,
SQL_TIME,
SQL_TIMESTAMP,
SQL_TINYINT,
SQL_VARBINARY,
SQL_VARCHAR,
0
};
Int4 sqltype_to_pgtype(SWORD fSqlType)
{
Int4 pgType;
switch(fSqlType) {
case SQL_BINARY:
pgType = PG_TYPE_BYTEA;
break;
case SQL_CHAR:
pgType = PG_TYPE_BPCHAR;
break;
case SQL_BIT:
pgType = globals.bools_as_char ? PG_TYPE_CHAR : PG_TYPE_BOOL;
break;
case SQL_DATE:
pgType = PG_TYPE_DATE;
break;
case SQL_DOUBLE:
case SQL_FLOAT:
pgType = PG_TYPE_FLOAT8;
break;
case SQL_INTEGER:
case SQL_BIGINT:
case SQL_NUMERIC:
case SQL_DECIMAL:
pgType = PG_TYPE_INT4;
break;
case SQL_LONGVARBINARY:
pgType = PG_TYPE_LO;
break;
case SQL_LONGVARCHAR:
pgType = globals.text_as_longvarchar ? PG_TYPE_TEXT : PG_TYPE_VARCHAR;
break;
case SQL_REAL:
pgType = PG_TYPE_FLOAT4;
break;
case SQL_SMALLINT:
case SQL_TINYINT:
pgType = PG_TYPE_INT2;
break;
case SQL_TIME:
pgType = PG_TYPE_TIME;
break;
case SQL_TIMESTAMP:
pgType = PG_TYPE_DATETIME;
break;
case SQL_VARBINARY:
pgType = PG_TYPE_BYTEA;
break;
case SQL_VARCHAR:
pgType = PG_TYPE_VARCHAR;
break;
default:
break;
}
return pgType;
}
/* There are two ways of calling this function:
1. When going through the supported PG types (SQLGetTypeInfo)
......@@ -94,7 +191,7 @@ Int2 pgtype_to_sqltype(StatementClass *stmt, Int4 type)
case PG_TYPE_CHAR16:
case PG_TYPE_NAME: return SQL_CHAR;
case PG_TYPE_BPCHAR: return SQL_CHAR; // temporary?
case PG_TYPE_BPCHAR: return SQL_CHAR;
case PG_TYPE_VARCHAR: return SQL_VARCHAR;
......@@ -165,7 +262,7 @@ char *pgtype_to_name(StatementClass *stmt, Int4 type)
case PG_TYPE_CHAR8: return "char8";
case PG_TYPE_CHAR16: return "char16";
case PG_TYPE_VARCHAR: return "varchar";
case PG_TYPE_BPCHAR: return "bpchar";
case PG_TYPE_BPCHAR: return "char";
case PG_TYPE_TEXT: return "text";
case PG_TYPE_NAME: return "name";
case PG_TYPE_INT2: return "int2";
......
......@@ -61,10 +61,12 @@
#define PG_TYPE_TIMESTAMP 1296
extern Int4 pgtypes_defined[];
extern Int2 sqlTypes[];
/* Defines for pgtype_precision */
#define PG_STATIC -1
Int4 sqltype_to_pgtype(Int2 fSqlType);
Int2 pgtype_to_sqltype(StatementClass *stmt, Int4 type);
Int2 pgtype_to_ctype(StatementClass *stmt, Int4 type);
......
......@@ -126,4 +126,3 @@ RETCODE SQL_API SQLDummyOrdinal(void)
return SQL_SUCCESS;
}
......@@ -40,7 +40,7 @@ typedef UInt4 Oid;
#define MAX_CONNECT_STRING 4096
#define ERROR_MSG_LENGTH 4096
#define FETCH_MAX 100 /* default number of rows to cache for declare/fetch */
#define FETCH_INCR 1000
#define TUPLE_MALLOC_INC 100
#define SOCK_BUFFER_SIZE 4096 /* default socket buffer size */
#define MAX_CONNECTIONS 128 /* conns per environment (arbitrary) */
#define MAX_FIELDS 512
......@@ -70,8 +70,8 @@ typedef UInt4 Oid;
/* Driver stuff */
#define DRIVERNAME "PostgreSQL ODBC"
#define DBMS_NAME "PostgreSQL"
#define DBMS_VERSION "06.40.0001 PostgreSQL 6.4"
#define POSTGRESDRIVERVERSION "06.40.0001"
#define DBMS_VERSION "06.40.0002 PostgreSQL 6.4"
#define POSTGRESDRIVERVERSION "06.40.0002"
#ifdef WIN32
#define DRIVER_FILE_NAME "PSQLODBC.DLL"
......@@ -116,12 +116,31 @@ typedef struct GlobalValues_
char bools_as_char;
char lie;
char parse;
char cancel_as_freestmt;
char extra_systable_prefixes[MEDIUM_REGISTRY_LEN];
char conn_settings[LARGE_REGISTRY_LEN];
FILE* mylogFP;
FILE* qlogFP;
} GLOBAL_VALUES;
typedef struct StatementOptions_ {
int maxRows;
int maxLength;
int rowset_size;
int keyset_size;
int cursor_type;
int scroll_concurrency;
int retrieve_data;
int bind_size; /* size of each structure if using Row Binding */
} StatementOptions;
/* Used to pass extra query info to send_query */
typedef struct QueryInfo_ {
int row_size;
QResultClass *result_in;
char *cursor;
} QueryInfo;
#define PG_TYPE_LO -999 /* hack until permanent type available */
#define PG_TYPE_LO_NAME "lo"
......
......@@ -101,6 +101,8 @@ BEGIN
WS_TABSTOP,140,35,80,10
CONTROL "&Use Declare/Fetch",DRV_USEDECLAREFETCH,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,15,50,80,10
CONTROL "Cancel as FreeStmt (Exp)",DRV_CANCELASFREESTMT,"Button",
BS_AUTOCHECKBOX | WS_TABSTOP,140,50,105,10
GROUPBOX "Unknown Sizes",IDC_STATIC,10,65,175,25
CONTROL "Maximum",DRV_UNKNOWN_MAX,"Button",BS_AUTORADIOBUTTON |
WS_GROUP | WS_TABSTOP,15,76,45,10
......@@ -202,8 +204,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 6,40,0,1
PRODUCTVERSION 6,40,0,1
FILEVERSION 6,40,0,2
PRODUCTVERSION 6,40,0,2
FILEFLAGSMASK 0x3L
#ifdef _DEBUG
FILEFLAGS 0x1L
......@@ -221,12 +223,12 @@ BEGIN
VALUE "Comments", "PostgreSQL ODBC driver for Windows 95\0"
VALUE "CompanyName", "Insight Distribution Systems\0"
VALUE "FileDescription", "PostgreSQL Driver\0"
VALUE "FileVersion", " 6.40.0001\0"
VALUE "FileVersion", " 6.40.0002\0"
VALUE "InternalName", "psqlodbc\0"
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.\0"
VALUE "OriginalFilename", "psqlodbc.dll\0"
VALUE "ProductName", "Microsoft Open Database Connectivity\0"
VALUE "ProductVersion", " 6.40.0001\0"
VALUE "ProductVersion", " 6.40.0002\0"
END
END
BLOCK "VarFileInfo"
......
......@@ -48,6 +48,30 @@ QR_set_num_fields(QResultClass *self, int new_num_fields)
mylog("exit QR_set_num_fields\n");
}
void
QR_set_position(QResultClass *self, int pos)
{
self->tupleField = self->backend_tuples + ((self->base + pos) * self->num_fields);
}
void
QR_set_cache_size(QResultClass *self, int cache_size)
{
self->cache_size = cache_size;
}
void
QR_set_rowset_size(QResultClass *self, int rowset_size)
{
self->rowset_size = rowset_size;
}
void
QR_inc_base(QResultClass *self, int base_inc)
{
self->base += base_inc;
}
/************************************/
/* CLASS QResult */
/************************************/
......@@ -77,9 +101,15 @@ QResultClass *rv;
rv->inTuples = FALSE;
rv->fcount = 0;
rv->fetch_count = 0;
rv->base = 0;
rv->currTuple = -1;
rv->num_fields = 0;
rv->tupleField = NULL;
rv->cursor = NULL;
rv->cache_size = globals.fetch_max;
rv->rowset_size = 1;
}
mylog("exit QR_Constructor\n");
......@@ -178,6 +208,8 @@ int num_fields = self->num_fields;
char
QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
{
int tuple_size;
// If called from send_query the first time (conn != NULL),
// then set the inTuples state,
// and read the tuples. If conn is NULL,
......@@ -186,7 +218,7 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
if (conn != NULL) {
self->conn = conn;
mylog("QR_fetch_tuples: cursor = '%s', self->cursor=%u\n", cursor, self->cursor);
mylog("QR_fetch_tuples: cursor = '%s', self->cursor=%u\n", (cursor==NULL)?"":cursor, self->cursor);
if (self->cursor)
free(self->cursor);
......@@ -199,7 +231,7 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
}
self->cursor = strdup(cursor);
}
// Read the field attributes.
// $$$$ Should do some error control HERE! $$$$
if ( CI_read_fields(self->fields, self->conn)) {
......@@ -214,9 +246,14 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
mylog("QR_fetch_tuples: past CI_read_fields: num_fields = %d\n", self->num_fields);
if (globals.use_declarefetch)
tuple_size = self->cache_size;
else
tuple_size = TUPLE_MALLOC_INC;
/* allocate memory for the tuple cache */
mylog("MALLOC: fetch_max = %d, size = %d\n", globals.fetch_max, self->num_fields * sizeof(TupleField) * globals.fetch_max);
self->backend_tuples = (TupleField *) malloc(self->num_fields * sizeof(TupleField) * globals.fetch_max);
mylog("MALLOC: tuple_size = %d, size = %d\n", tuple_size, self->num_fields * sizeof(TupleField) * tuple_size);
self->backend_tuples = (TupleField *) malloc(self->num_fields * sizeof(TupleField) * tuple_size);
if ( ! self->backend_tuples) {
self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Could not get memory for tuple cache.");
......@@ -225,13 +262,16 @@ QR_fetch_tuples(QResultClass *self, ConnectionClass *conn, char *cursor)
self->inTuples = TRUE;
/* Force a read to occur in next_tuple */
self->fcount = globals.fetch_max+1;
self->fetch_count = globals.fetch_max+1;
self->fcount = tuple_size+1;
self->fetch_count = tuple_size+1;
self->base = 0;
return QR_next_tuple(self);
}
else {
// Always have to read the field attributes.
// But we dont have to reallocate memory for them!
......@@ -257,9 +297,10 @@ QResultClass *res;
sprintf(buf, "close %s", self->cursor);
mylog("QResult: closing cursor: '%s'\n", buf);
res = CC_send_query(self->conn, buf, NULL, NULL);
res = CC_send_query(self->conn, buf, NULL);
self->inTuples = FALSE;
self->currTuple = -1;
free(self->cursor);
self->cursor = NULL;
......@@ -274,7 +315,7 @@ QResultClass *res;
if (CC_cursor_count(self->conn) == 0) {
mylog("QResult: END transaction on conn=%u\n", self->conn);
res = CC_send_query(self->conn, "END", NULL, NULL);
res = CC_send_query(self->conn, "END", NULL);
CC_set_no_trans(self->conn);
......@@ -300,9 +341,14 @@ SocketClass *sock;
/* Speed up access */
int fetch_count = self->fetch_count;
int fcount = self->fcount;
int fetch_size, offset= 0;
int end_tuple = self->rowset_size + self->base;
char corrected = FALSE;
TupleField *the_tuples = self->backend_tuples;
static char msgbuffer[MAX_MESSAGE_LEN+1];
char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont need static
char fetch[128];
QueryInfo qi;
if (fetch_count < fcount) { /* return a row from cache */
mylog("next_tuple: fetch_count < fcount: returning tuple %d, fcount = %d\n", fetch_count, fcount);
......@@ -310,9 +356,9 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
self->fetch_count++;
return TRUE;
}
else if (self->fcount < globals.fetch_max) { /* last row from cache */
// We are done because we didn't even get FETCH_MAX tuples
mylog("next_tuple: fcount < FETCH_MAX: fcount = %d, fetch_count = %d\n", fcount, fetch_count);
else if (self->fcount < 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", fcount, fetch_count);
self->tupleField = NULL;
self->status = PGRES_END_TUPLES;
return -1; /* end of tuples */
......@@ -324,8 +370,8 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
and read the tuples.
*/
self->tupleField = NULL;
if ( ! self->inTuples) {
char fetch[128];
if ( ! globals.use_declarefetch) {
mylog("next_tuple: ALL_ROWS: done, fcount = %d, fetch_count = %d\n", fcount, fetch_count);
......@@ -334,20 +380,54 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
return -1; /* end of tuples */
}
sprintf(fetch, "fetch %d in %s", globals.fetch_max, self->cursor);
if (self->base == fcount) { /* not a correction */
/* Determine the optimum cache size. */
if (globals.fetch_max % self->rowset_size == 0)
fetch_size = globals.fetch_max;
else if (self->rowset_size < globals.fetch_max)
fetch_size = (globals.fetch_max / self->rowset_size) * self->rowset_size;
else
fetch_size = self->rowset_size;
self->cache_size = fetch_size;
self->fetch_count = 1;
}
else { /* need to correct */
corrected = TRUE;
fetch_size = end_tuple - fcount;
self->cache_size += fetch_size;
offset = self->fetch_count;
self->fetch_count++;
}
self->backend_tuples = (TupleField *) realloc(self->backend_tuples, self->num_fields * sizeof(TupleField) * self->cache_size);
if ( ! self->backend_tuples) {
self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Out of memory while reading tuples.");
return FALSE;
}
sprintf(fetch, "fetch %d in %s", fetch_size, self->cursor);
mylog("next_tuple: sending actual fetch (%d) query '%s'\n", globals.fetch_max, fetch);
mylog("next_tuple: sending actual fetch (%d) query '%s'\n", fetch_size, fetch);
// don't read ahead for the next tuple (self) !
res = CC_send_query(self->conn, fetch, self, NULL);
qi.row_size = self->cache_size;
qi.result_in = self;
qi.cursor = NULL;
res = CC_send_query(self->conn, fetch, &qi);
if (res == NULL) {
self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Error fetching next group.");
return FALSE;
}
self->inTuples = TRUE;
/* This is a true fetch, like SQLFetch() */
self->fetch_count = 1;
}
else {
mylog("next_tuple: inTuples = true, falling through: fcount = %d, fetch_count = %d\n", self->fcount, self->fetch_count);
......@@ -357,11 +437,14 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
*/
self->fetch_count = 0;
}
// fall through and read the next group
}
if ( ! corrected) {
self->base = 0;
self->fcount = 0;
}
self->fcount = 0;
sock = CC_get_socket(self->conn);
self->tupleField = NULL;
......@@ -377,11 +460,11 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
case 'B': /* Tuples in binary format */
case 'D': /* Tuples in ASCII format */
if ( ! globals.use_declarefetch && self->fcount > 0 && ! (self->fcount % globals.fetch_max)) {
if ( ! globals.use_declarefetch && self->fcount > 0 && ! (self->fcount % TUPLE_MALLOC_INC)) {
size_t old_size = self->fcount * self->num_fields * sizeof(TupleField);
mylog("REALLOC: old_size = %d\n", old_size);
self->backend_tuples = (TupleField *) realloc(self->backend_tuples, old_size + (self->num_fields * sizeof(TupleField) * globals.fetch_max));
self->backend_tuples = (TupleField *) realloc(self->backend_tuples, old_size + (self->num_fields * sizeof(TupleField) * TUPLE_MALLOC_INC));
if ( ! self->backend_tuples) {
self->status = PGRES_FATAL_ERROR;
QR_set_message(self, "Out of memory while reading tuples.");
......@@ -412,7 +495,7 @@ char cmdbuffer[MAX_MESSAGE_LEN+1]; // QR_set_command() dups this string so dont
mylog("_next_tuple: 'C' fetch_max && fcount = %d\n", self->fcount);
/* set to first row */
self->tupleField = self->backend_tuples; // the_tuples;
self->tupleField = self->backend_tuples + (offset * self->num_fields);
return TRUE;
}
else { // We are surely done here (we read 0 tuples)
......@@ -532,5 +615,6 @@ ColumnInfoClass *flds;
} else
bmp <<= 1;
}
self->currTuple++;
return TRUE;
}
......@@ -44,8 +44,13 @@ struct QResultClass_ {
// Stuff for declare/fetch tuples
int fetch_count; // logical rows read so far
int fcount; // actual rows read in the fetch
int currTuple;
int base;
int num_fields; // number of fields in the result
int cache_size;
int rowset_size;
QueryResultCode status;
char *message;
......@@ -93,7 +98,7 @@ struct QResultClass_ {
#define QR_get_status(self) (self->status)
// Core Functions
QResultClass *QR_Constructor(void);
QResultClass *QR_Constructor();
void QR_Destructor(QResultClass *self);
char QR_read_tuple(QResultClass *self, char binary);
int QR_next_tuple(QResultClass *self);
......@@ -104,5 +109,10 @@ void QR_set_command(QResultClass *self, char *msg);
void QR_set_notice(QResultClass *self, char *msg);
void QR_set_num_fields(QResultClass *self, int new_num_fields); /* manual result only */
void QR_inc_base(QResultClass *self, int base_inc);
void QR_set_cache_size(QResultClass *self, int cache_size);
void QR_set_rowset_size(QResultClass *self, int rowset_size);
void QR_set_position(QResultClass *self, int pos);
#endif
......@@ -44,6 +44,7 @@
#define DRV_EXTRASYSTABLEPREFIXES 1051
#define DS_ROWVERSIONING 1052
#define DRV_PARSE 1052
#define DRV_CANCELASFREESTMT 1053
#define IDC_OPTIONS 1054
#define DRV_KSQO 1055
#define DS_PG64 1057
......
This diff is collapsed.
......@@ -21,8 +21,18 @@
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#define closesocket(xxx) close(xxx)
#define SOCKETFD int
#ifndef INADDR_NONE
#ifndef _IN_ADDR_T
#define _IN_ADDR_T
typedef unsigned int in_addr_t;
#endif
#define INADDR_NONE ((in_addr_t)-1)
#endif
#else
#include <winsock.h>
#define SOCKETFD SOCKET
......
......@@ -97,6 +97,14 @@ StatementClass *stmt;
*phstmt = (HSTMT) stmt;
/* Copy default statement options based from Connection options
*/
stmt->options = conn->stmtOptions;
/* Save the handle for later */
stmt->phstmt = phstmt;
return SQL_SUCCESS;
}
......@@ -166,6 +174,18 @@ StatementClass *stmt = (StatementClass *) hstmt;
/**********************************************************************
* StatementClass implementation
*/
void
InitializeStatementOptions(StatementOptions *opt)
{
opt->maxRows = 0; // driver returns all rows
opt->maxLength = 0; // driver returns all data for char/binary
opt->rowset_size = 1;
opt->keyset_size = 0; // fully keyset driven is the default
opt->scroll_concurrency = SQL_CONCUR_READ_ONLY;
opt->cursor_type = SQL_CURSOR_FORWARD_ONLY;
opt->bind_size = 0; /* default is to bind by column */
opt->retrieve_data = SQL_RD_ON;
}
StatementClass *
SC_Constructor(void)
......@@ -175,40 +195,51 @@ StatementClass *rv;
rv = (StatementClass *) malloc(sizeof(StatementClass));
if (rv) {
rv->hdbc = NULL; /* no connection associated yet */
rv->phstmt = NULL;
rv->result = NULL;
rv->manual_result = FALSE;
rv->prepare = FALSE;
rv->status = STMT_ALLOCATED;
rv->maxRows = 0; // driver returns all rows
rv->rowset_size = 1;
rv->keyset_size = 0; // fully keyset driven is the default
rv->scroll_concurrency = SQL_CONCUR_READ_ONLY;
rv->cursor_type = SQL_CURSOR_FORWARD_ONLY;
rv->internal = FALSE;
rv->errormsg = NULL;
rv->errornumber = 0;
rv->errormsg_created = FALSE;
rv->statement = NULL;
rv->stmt_with_params[0] = '\0';
rv->statement_type = STMT_TYPE_UNKNOWN;
rv->bindings = NULL;
rv->bindings_allocated = 0;
rv->parameters_allocated = 0;
rv->parameters = 0;
rv->currTuple = -1;
rv->rowset_start = -1;
rv->current_col = -1;
rv->result = 0;
rv->bind_row = 0;
rv->last_fetch_count = 0;
rv->save_rowset_size = -1;
rv->data_at_exec = -1;
rv->current_exec_param = -1;
rv->put_data = FALSE;
rv->lobj_fd = -1;
rv->internal = FALSE;
rv->cursor_name[0] = '\0';
/* Parse Stuff */
rv->ti = NULL;
rv->fi = NULL;
rv->ntab = 0;
rv->nfld = 0;
rv->parse_status = STMT_PARSE_NONE;
/* Clear Statement Options -- defaults will be set in AllocStmt */
memset(&rv->options, 0, sizeof(StatementOptions));
}
return rv;
}
......@@ -361,7 +392,7 @@ mylog("recycle statement: self= %u\n", self);
conn = SC_get_conn(self);
if ( ! CC_is_in_autocommit(conn) && CC_is_in_trans(conn)) {
CC_send_query(conn, "ABORT", NULL, NULL);
CC_send_query(conn, "ABORT", NULL);
CC_set_no_trans(conn);
}
break;
......@@ -405,11 +436,18 @@ mylog("recycle statement: self= %u\n", self);
self->result = NULL;
}
/****************************************************************/
/* Reset only parameters that have anything to do with results */
/****************************************************************/
self->status = STMT_READY;
self->manual_result = FALSE; // very important
self->currTuple = -1;
self->rowset_start = -1;
self->current_col = -1;
self->bind_row = 0;
self->last_fetch_count = 0;
self->errormsg = NULL;
self->errornumber = 0;
......@@ -451,6 +489,7 @@ SC_unbind_cols(StatementClass *self)
Int2 lf;
for(lf = 0; lf < self->bindings_allocated; lf++) {
self->bindings[lf].data_left = -1;
self->bindings[lf].buflen = 0;
self->bindings[lf].buffer = NULL;
self->bindings[lf].used = NULL;
......@@ -534,6 +573,7 @@ ConnectionClass *conn;
QResultClass *res;
char ok, was_ok, was_nonfatal;
Int2 oldstatus, numcols;
QueryInfo qi;
conn = SC_get_conn(self);
......@@ -545,7 +585,7 @@ Int2 oldstatus, numcols;
if ( ! self->internal && ! CC_is_in_trans(conn) && (globals.use_declarefetch || STMT_UPDATE(self))) {
mylog(" about to begin a transaction on statement = %u\n", self);
res = CC_send_query(conn, "BEGIN", NULL, NULL);
res = CC_send_query(conn, "BEGIN", NULL);
if ( ! res) {
self->errormsg = "Could not begin a transaction";
self->errornumber = STMT_EXEC_ERROR;
......@@ -587,14 +627,26 @@ Int2 oldstatus, numcols;
/* send the declare/select */
self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
self->result = CC_send_query(conn, self->stmt_with_params, NULL);
if (globals.use_declarefetch && self->result != NULL) {
QR_Destructor(self->result);
/* That worked, so now send the fetch to start getting data back */
sprintf(fetch, "fetch %d in %s", globals.fetch_max, self->cursor_name);
qi.result_in = NULL;
qi.cursor = self->cursor_name;
qi.row_size = globals.fetch_max;
/* Most likely the rowset size will not be set by the application until
after the statement is executed, so might as well use the cache size.
The qr_next_tuple() function will correct for any discrepancies in
sizes and adjust the cache accordingly.
*/
sprintf(fetch, "fetch %d in %s", qi.row_size, self->cursor_name);
// Save the cursor in the result for later use
self->result = CC_send_query( conn, fetch, NULL, self->cursor_name);
self->result = CC_send_query( conn, fetch, &qi);
}
mylog(" done sending the query:\n");
......@@ -604,11 +656,11 @@ Int2 oldstatus, numcols;
}
else { // not a SELECT statement so don't use a cursor
mylog(" its NOT a select statement: stmt=%u\n", self);
self->result = CC_send_query(conn, self->stmt_with_params, NULL, NULL);
self->result = CC_send_query(conn, self->stmt_with_params, NULL);
// If we are in autocommit, we must send the commit.
if ( ! self->internal && CC_is_in_autocommit(conn) && STMT_UPDATE(self)) {
CC_send_query(conn, "COMMIT", NULL, NULL);
CC_send_query(conn, "COMMIT", NULL);
CC_set_no_trans(conn);
}
......@@ -630,6 +682,7 @@ Int2 oldstatus, numcols;
self->currTuple = -1; /* set cursor before the first tuple in the list */
self->current_col = -1;
self->rowset_start = -1;
/* see if the query did return any result columns */
numcols = QR_NumResultCols(self->result);
......@@ -692,7 +745,7 @@ SC_log_error(char *func, char *desc, StatementClass *self)
qlog(" stmt_with_params='%s'\n", self->stmt_with_params);
qlog(" data_at_exec=%d, current_exec_param=%d, put_data=%d\n", self->data_at_exec, self->current_exec_param, self->put_data);
qlog(" currTuple=%d, current_col=%d, lobj_fd=%d\n", self->currTuple, self->current_col, self->lobj_fd);
qlog(" maxRows=%d, rowset_size=%d, keyset_size=%d, cursor_type=%d, scroll_concurrency=%d\n", self->maxRows, self->rowset_size, self->keyset_size, self->cursor_type, self->scroll_concurrency);
qlog(" maxRows=%d, rowset_size=%d, keyset_size=%d, cursor_type=%d, scroll_concurrency=%d\n", self->options.maxRows, self->options.rowset_size, self->options.keyset_size, self->options.cursor_type, self->options.scroll_concurrency);
qlog(" cursor_name='%s'\n", self->cursor_name);
qlog(" ----------------QResult Info -------------------------------\n");
......
......@@ -66,6 +66,10 @@ typedef enum {
#define STMT_CREATE_TABLE_ERROR 17
#define STMT_NO_CURSOR_NAME 18
#define STMT_INVALID_CURSOR_NAME 19
#define STMT_INVALID_ARGUMENT_NO 20
#define STMT_ROW_OUT_OF_RANGE 21
#define STMT_OPERATION_CANCELLED 22
#define STMT_INVALID_CURSOR_POSITION 23
/* statement types */
enum {
......@@ -93,6 +97,13 @@ enum {
STMT_PARSE_FATAL,
};
/* Result style */
enum {
STMT_FETCH_NONE = 0,
STMT_FETCH_NORMAL,
STMT_FETCH_EXTENDED,
};
typedef struct {
COL_INFO *col_info; /* cached SQLColumns info for this table */
char name[MAX_TABLE_LEN+1];
......@@ -117,21 +128,16 @@ typedef struct {
} FIELD_INFO;
/******** Statement Handle ***********/
struct StatementClass_ {
ConnectionClass *hdbc; /* pointer to ConnectionClass this statement belongs to */
QResultClass *result; /* result of the current statement */
HSTMT FAR *phstmt;
StatementOptions options;
STMT_Status status;
char *errormsg;
int errornumber;
int maxRows;
int rowset_size;
int keyset_size;
int cursor_type;
int scroll_concurrency;
/* information on bindings */
BindInfoClass *bindings; /* array to store the binding information */
......@@ -141,7 +147,11 @@ struct StatementClass_ {
int parameters_allocated;
ParameterInfoClass *parameters;
Int4 currTuple;
Int4 currTuple; /* current absolute row number (GetData, SetPos, SQLFetch) */
int save_rowset_size; /* saved rowset size in case of change/FETCH_NEXT */
int rowset_start; /* start of rowset (an absolute row number) */
int bind_row; /* current offset for Multiple row/column binding */
int last_fetch_count; /* number of rows retrieved in last fetch/extended fetch */
int current_col; /* current column for GetData -- used to handle multiple calls */
int lobj_fd; /* fd of the current large object */
......@@ -181,6 +191,7 @@ struct StatementClass_ {
/* Statement prototypes */
StatementClass *SC_Constructor(void);
void InitializeStatementOptions(StatementOptions *opt);
char SC_Destructor(StatementClass *self);
int statement_type(char *statement);
char parse_statement(StatementClass *stmt);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment