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