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

This commit is contained in:
Byron Nikolaidis 1998-12-29 01:49:58 +00:00
parent d73cf044f6
commit 6d7735e7f0
26 changed files with 1568 additions and 572 deletions

View File

@ -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);
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;
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;

View File

@ -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...) */

View File

@ -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;
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;
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";
/* 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";
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;
'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
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
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
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
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];
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;
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;
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)
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);
}
*/

View File

@ -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,
/******* 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);
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);

View File

@ -101,18 +101,38 @@ copy_and_convert_field_bindinfo(StatementClass *stmt, Int4 field_type, void *val
BindInfoClass *bic = &(stmt->bindings[col]); BindInfoClass *bic = &(stmt->bindings[col]);
return copy_and_convert_field(stmt, field_type, value, (Int2)bic->returntype, (PTR)bic->buffer, return copy_and_convert_field(stmt, field_type, value, (Int2)bic->returntype, (PTR)bic->buffer,
(SDWORD)bic->buflen, (SDWORD *)bic->used, FALSE); (SDWORD)bic->buflen, (SDWORD *)bic->used);
} }
/* This is called by SQLGetData() */ /* This is called by SQLGetData() */
int int
copy_and_convert_field(StatementClass *stmt, Int4 field_type, void *value, Int2 fCType, 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)
{ {
Int4 len = 0; Int4 len = 0, copy_len = 0;
SIMPLE_TIME st; SIMPLE_TIME st;
time_t t = time(NULL); time_t t = time(NULL);
struct tm *tim; struct tm *tim;
int pcbValueOffset, rgbValueOffset;
char *rgbValueBindRow, *ptr;
int bind_row = stmt->bind_row;
int bind_size = stmt->options.bind_size;
int result = COPY_OK;
char tempBuf[TEXT_FIELD_SIZE+5];
/* rgbValueOffset is *ONLY* for character and binary data */
/* pcbValueOffset is for computing any pcbValue location */
if (bind_size > 0) {
pcbValueOffset = rgbValueOffset = (bind_size * bind_row);
}
else {
pcbValueOffset = bind_row * sizeof(SDWORD);
rgbValueOffset = bind_row * cbValueMax;
}
memset(&st, 0, sizeof(SIMPLE_TIME)); memset(&st, 0, sizeof(SIMPLE_TIME));
@ -122,13 +142,13 @@ struct tm *tim;
st.d = tim->tm_mday; st.d = tim->tm_mday;
st.y = tim->tm_year + 1900; st.y = tim->tm_year + 1900;
mylog("copy_and_convert: field_type = %d, fctype = %d, value = '%s', cbValueMax=%d\n", field_type, fCType, value, cbValueMax); mylog("copy_and_convert: field_type = %d, fctype = %d, value = '%s', cbValueMax=%d\n", field_type, fCType, (value==NULL)?"<NULL>":value, cbValueMax);
if ( ! value) { if ( ! value) {
/* handle a null just by returning SQL_NULL_DATA in pcbValue, */ /* handle a null just by returning SQL_NULL_DATA in pcbValue, */
/* and doing nothing to the buffer. */ /* and doing nothing to the buffer. */
if(pcbValue) { if(pcbValue) {
*pcbValue = SQL_NULL_DATA; *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = SQL_NULL_DATA;
} }
return COPY_OK; return COPY_OK;
} }
@ -191,7 +211,7 @@ struct tm *tim;
/* This is for internal use by SQLStatistics() */ /* This is for internal use by SQLStatistics() */
case PG_TYPE_INT28: { case PG_TYPE_INT28: {
// this is an array of eight integers // this is an array of eight integers
short *short_array = (short *)rgbValue; short *short_array = (short *) ( (char *) rgbValue + rgbValueOffset);
len = 16; len = 16;
@ -207,7 +227,7 @@ struct tm *tim;
/* There is no corresponding fCType for this. */ /* There is no corresponding fCType for this. */
if(pcbValue) if(pcbValue)
*pcbValue = len; *(SDWORD *) ((char *) pcbValue + pcbValueOffset) = len;
return COPY_OK; /* dont go any further or the data will be trashed */ return COPY_OK; /* dont go any further or the data will be trashed */
} }
@ -215,12 +235,12 @@ struct tm *tim;
/* This is a large object OID, which is used to store LONGVARBINARY objects. */ /* This is a large object OID, which is used to store LONGVARBINARY objects. */
case PG_TYPE_LO: case PG_TYPE_LO:
return convert_lo( stmt, value, fCType, rgbValue, cbValueMax, pcbValue, multiple); return convert_lo( stmt, value, fCType, ((char *) rgbValue + rgbValueOffset), cbValueMax, (SDWORD *) ((char *) pcbValue + pcbValueOffset));
default: default:
if (field_type == stmt->hdbc->lobj_type) /* hack until permanent type available */ if (field_type == stmt->hdbc->lobj_type) /* hack until permanent type available */
return convert_lo( stmt, value, fCType, rgbValue, cbValueMax, pcbValue, multiple); return convert_lo( stmt, value, fCType, ((char *) rgbValue + rgbValueOffset), cbValueMax, (SDWORD *) ((char *) pcbValue + pcbValueOffset));
} }
/* Change default into something useable */ /* Change default into something useable */
@ -231,21 +251,24 @@ struct tm *tim;
} }
rgbValueBindRow = (char *) rgbValue + rgbValueOffset;
if(fCType == SQL_C_CHAR) { if(fCType == SQL_C_CHAR) {
/* Special character formatting as required */ /* Special character formatting as required */
/* These really should return error if cbValueMax is not big enough. */ /* These really should return error if cbValueMax is not big enough. */
switch(field_type) { switch(field_type) {
case PG_TYPE_DATE: case PG_TYPE_DATE:
len = 10; len = 10;
if (cbValueMax > len) if (cbValueMax > len)
sprintf((char *)rgbValue, "%.4d-%.2d-%.2d", st.y, st.m, st.d); sprintf(rgbValueBindRow, "%.4d-%.2d-%.2d", st.y, st.m, st.d);
break; break;
case PG_TYPE_TIME: case PG_TYPE_TIME:
len = 8; len = 8;
if (cbValueMax > len) if (cbValueMax > len)
sprintf((char *)rgbValue, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss); sprintf(rgbValueBindRow, "%.2d:%.2d:%.2d", st.hh, st.mm, st.ss);
break; break;
case PG_TYPE_ABSTIME: case PG_TYPE_ABSTIME:
@ -253,15 +276,15 @@ struct tm *tim;
case PG_TYPE_TIMESTAMP: case PG_TYPE_TIMESTAMP:
len = 19; len = 19;
if (cbValueMax > len) if (cbValueMax > len)
sprintf((char *) rgbValue, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d", sprintf(rgbValueBindRow, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d",
st.y, st.m, st.d, st.hh, st.mm, st.ss); st.y, st.m, st.d, st.hh, st.mm, st.ss);
break; break;
case PG_TYPE_BOOL: case PG_TYPE_BOOL:
len = 1; len = 1;
if (cbValueMax > len) { if (cbValueMax > len) {
strcpy((char *) rgbValue, value); strcpy(rgbValueBindRow, value);
mylog("PG_TYPE_BOOL: rgbValue = '%s'\n", rgbValue); mylog("PG_TYPE_BOOL: rgbValueBindRow = '%s'\n", rgbValueBindRow);
} }
break; break;
@ -276,15 +299,48 @@ struct tm *tim;
object used to store those. object used to store those.
*/ */
case PG_TYPE_BYTEA: // convert binary data to hex strings (i.e, 255 = "FF") case PG_TYPE_BYTEA: // convert binary data to hex strings (i.e, 255 = "FF")
len = convert_pgbinary_to_char(value, rgbValue, cbValueMax); len = convert_pgbinary_to_char(value, rgbValueBindRow, cbValueMax);
/***** THIS IS NOT PROPERLY IMPLEMENTED *****/
break; break;
default: default:
/* convert linefeeds to carriage-return/linefeed */ /* convert linefeeds to carriage-return/linefeed */
convert_linefeeds( (char *) value, rgbValue, cbValueMax); len = convert_linefeeds(value, tempBuf, sizeof(tempBuf));
len = strlen(rgbValue); ptr = tempBuf;
mylog(" SQL_C_CHAR, default: len = %d, cbValueMax = %d, rgbValue = '%s'\n", len, cbValueMax, rgbValue); mylog("DEFAULT: len = %d, ptr = '%s'\n", len, ptr);
if (stmt->current_col >= 0) {
if (stmt->bindings[stmt->current_col].data_left == 0)
return COPY_NO_DATA_FOUND;
else if (stmt->bindings[stmt->current_col].data_left > 0) {
ptr += len - stmt->bindings[stmt->current_col].data_left;
len = stmt->bindings[stmt->current_col].data_left;
}
else
stmt->bindings[stmt->current_col].data_left = strlen(value);
}
if (cbValueMax > 0) {
copy_len = (len >= cbValueMax) ? cbValueMax -1 : len;
/* Copy the data */
strncpy_null(rgbValueBindRow, ptr, copy_len + 1);
/* Adjust data_left for next time */
if (stmt->current_col >= 0) {
stmt->bindings[stmt->current_col].data_left -= copy_len;
}
}
/* Finally, check for truncation so that proper status can be returned */
if ( len >= cbValueMax)
result = COPY_RESULT_TRUNCATED;
mylog(" SQL_C_CHAR, default: len = %d, cbValueMax = %d, rgbValueBindRow = '%s'\n", len, cbValueMax, rgbValueBindRow);
break; break;
} }
@ -301,7 +357,13 @@ struct tm *tim;
case SQL_C_DATE: case SQL_C_DATE:
len = 6; len = 6;
{ {
DATE_STRUCT *ds = (DATE_STRUCT *) rgbValue; DATE_STRUCT *ds;
if (bind_size > 0) {
ds = (DATE_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
} else {
ds = (DATE_STRUCT *) rgbValue + bind_row;
}
ds->year = st.y; ds->year = st.y;
ds->month = st.m; ds->month = st.m;
ds->day = st.d; ds->day = st.d;
@ -311,7 +373,13 @@ struct tm *tim;
case SQL_C_TIME: case SQL_C_TIME:
len = 6; len = 6;
{ {
TIME_STRUCT *ts = (TIME_STRUCT *) rgbValue; TIME_STRUCT *ts;
if (bind_size > 0) {
ts = (TIME_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
} else {
ts = (TIME_STRUCT *) rgbValue + bind_row;
}
ts->hour = st.hh; ts->hour = st.hh;
ts->minute = st.mm; ts->minute = st.mm;
ts->second = st.ss; ts->second = st.ss;
@ -321,7 +389,12 @@ struct tm *tim;
case SQL_C_TIMESTAMP: case SQL_C_TIMESTAMP:
len = 16; len = 16;
{ {
TIMESTAMP_STRUCT *ts = (TIMESTAMP_STRUCT *) rgbValue; TIMESTAMP_STRUCT *ts;
if (bind_size > 0) {
ts = (TIMESTAMP_STRUCT *) ((char *) rgbValue + (bind_row * bind_size));
} else {
ts = (TIMESTAMP_STRUCT *) rgbValue + bind_row;
}
ts->year = st.y; ts->year = st.y;
ts->month = st.m; ts->month = st.m;
ts->day = st.d; ts->day = st.d;
@ -334,59 +407,132 @@ struct tm *tim;
case SQL_C_BIT: case SQL_C_BIT:
len = 1; len = 1;
*((UCHAR *)rgbValue) = atoi(value); if (bind_size > 0) {
mylog("SQL_C_BIT: val = %d, cb = %d, rgb=%d\n", atoi(value), cbValueMax, *((UCHAR *)rgbValue)); *(UCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
} else {
*((UCHAR *)rgbValue + bind_row) = atoi(value);
}
// mylog("SQL_C_BIT: val = %d, cb = %d, rgb=%d\n", atoi(value), cbValueMax, *((UCHAR *)rgbValue));
break; break;
case SQL_C_STINYINT: case SQL_C_STINYINT:
case SQL_C_TINYINT: case SQL_C_TINYINT:
len = 1; len = 1;
*((SCHAR *) rgbValue) = atoi(value); if (bind_size > 0) {
*(SCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
} else {
*((SCHAR *) rgbValue + bind_row) = atoi(value);
}
break; break;
case SQL_C_UTINYINT: case SQL_C_UTINYINT:
len = 1; len = 1;
*((UCHAR *) rgbValue) = atoi(value); if (bind_size > 0) {
*(UCHAR *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
} else {
*((UCHAR *) rgbValue + bind_row) = atoi(value);
}
break; break;
case SQL_C_FLOAT: case SQL_C_FLOAT:
len = 4; len = 4;
*((SFLOAT *)rgbValue) = (float) atof(value); if (bind_size > 0) {
*(SFLOAT *) ((char *) rgbValue + (bind_row * bind_size)) = (float) atof(value);
} else {
*((SFLOAT *)rgbValue + bind_row) = (float) atof(value);
}
break; break;
case SQL_C_DOUBLE: case SQL_C_DOUBLE:
len = 8; len = 8;
*((SDOUBLE *)rgbValue) = atof(value); if (bind_size > 0) {
*(SDOUBLE *) ((char *) rgbValue + (bind_row * bind_size)) = atof(value);
} else {
*((SDOUBLE *)rgbValue + bind_row) = atof(value);
}
break; break;
case SQL_C_SSHORT: case SQL_C_SSHORT:
case SQL_C_SHORT: case SQL_C_SHORT:
len = 2; len = 2;
*((SWORD *)rgbValue) = atoi(value); if (bind_size > 0) {
*(SWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
} else {
*((SWORD *)rgbValue + bind_row) = atoi(value);
}
break; break;
case SQL_C_USHORT: case SQL_C_USHORT:
len = 2; len = 2;
*((UWORD *)rgbValue) = atoi(value); if (bind_size > 0) {
*(UWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atoi(value);
} else {
*((UWORD *)rgbValue + bind_row) = atoi(value);
}
break; break;
case SQL_C_SLONG: case SQL_C_SLONG:
case SQL_C_LONG: case SQL_C_LONG:
len = 4; len = 4;
*((SDWORD *)rgbValue) = atol(value); if (bind_size > 0) {
*(SDWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atol(value);
} else {
*((SDWORD *)rgbValue + bind_row) = atol(value);
}
break; break;
case SQL_C_ULONG: case SQL_C_ULONG:
len = 4; len = 4;
*((UDWORD *)rgbValue) = atol(value); if (bind_size > 0) {
*(UDWORD *) ((char *) rgbValue + (bind_row * bind_size)) = atol(value);
} else {
*((UDWORD *)rgbValue + bind_row) = atol(value);
}
break; break;
case SQL_C_BINARY: case SQL_C_BINARY:
// truncate if necessary // truncate if necessary
// convert octal escapes to bytes // convert octal escapes to bytes
len = convert_from_pgbinary(value, rgbValue, cbValueMax);
mylog("SQL_C_BINARY: len = %d\n", len); len = convert_from_pgbinary(value, tempBuf, sizeof(tempBuf));
ptr = tempBuf;
if (stmt->current_col >= 0) {
/* No more data left for this column */
if (stmt->bindings[stmt->current_col].data_left == 0)
return COPY_NO_DATA_FOUND;
/* Second (or more) call to SQLGetData so move the pointer */
else if (stmt->bindings[stmt->current_col].data_left > 0) {
ptr += len - stmt->bindings[stmt->current_col].data_left;
len = stmt->bindings[stmt->current_col].data_left;
}
/* First call to SQLGetData so initialize data_left */
else
stmt->bindings[stmt->current_col].data_left = len;
}
if (cbValueMax > 0) {
copy_len = (len > cbValueMax) ? cbValueMax : len;
/* Copy the data */
memcpy(rgbValueBindRow, ptr, copy_len);
/* Adjust data_left for next time */
if (stmt->current_col >= 0) {
stmt->bindings[stmt->current_col].data_left -= copy_len;
}
}
/* Finally, check for truncation so that proper status can be returned */
if ( len > cbValueMax)
result = COPY_RESULT_TRUNCATED;
mylog("SQL_C_BINARY: len = %d, copy_len = %d\n", len, copy_len);
break; break;
default: default:
@ -395,10 +541,11 @@ struct tm *tim;
} }
// store the length of what was copied, if there's a place for it // store the length of what was copied, if there's a place for it
if(pcbValue) if(pcbValue) {
*pcbValue = len; *(SDWORD *) ((char *)pcbValue + pcbValueOffset) = len;
}
return COPY_OK; return result;
} }
@ -423,6 +570,8 @@ struct tm *tim;
SDWORD used; SDWORD used;
char *buffer, *buf; char *buffer, *buf;
char in_quote = FALSE; char in_quote = FALSE;
Oid lobj_oid;
int lobj_fd, retval;
if ( ! old_statement) { if ( ! old_statement) {
@ -731,18 +880,50 @@ char in_quote = FALSE;
case SQL_VARBINARY: /* non-ascii characters should be converted to octal */ case SQL_VARBINARY: /* non-ascii characters should be converted to octal */
new_statement[npos++] = '\''; /* Open Quote */ new_statement[npos++] = '\''; /* Open Quote */
mylog("SQL_LONGVARBINARY: about to call convert_to_pgbinary, used = %d\n", used); mylog("SQL_VARBINARY: about to call convert_to_pgbinary, used = %d\n", used);
npos += convert_to_pgbinary(buf, &new_statement[npos], used); npos += convert_to_pgbinary(buf, &new_statement[npos], used);
new_statement[npos++] = '\''; /* Close Quote */ new_statement[npos++] = '\''; /* Close Quote */
break; break;
case SQL_LONGVARBINARY: case SQL_LONGVARBINARY:
if ( stmt->parameters[param_number].data_at_exec) {
lobj_oid = stmt->parameters[param_number].lobj_oid;
}
else {
/* store the oid */
lobj_oid = lo_creat(stmt->hdbc, INV_READ | INV_WRITE);
if (lobj_oid == 0) {
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Couldnt create (in-line) large object.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
/* store the fd */
lobj_fd = lo_open(stmt->hdbc, lobj_oid, INV_WRITE);
if ( lobj_fd < 0) {
stmt->errornumber = STMT_EXEC_ERROR;
stmt->errormsg = "Couldnt open (in-line) large object for writing.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
retval = lo_write(stmt->hdbc, lobj_fd, buffer, used);
lo_close(stmt->hdbc, lobj_fd);
}
/* the oid of the large object -- just put that in for the /* the oid of the large object -- just put that in for the
parameter marker -- the data has already been sent to the large object parameter marker -- the data has already been sent to the large object
*/ */
sprintf(param_string, "%d", stmt->parameters[param_number].lobj_oid); sprintf(param_string, "%d", lobj_oid);
strcpy(&new_statement[npos], param_string); strcpy(&new_statement[npos], param_string);
npos += strlen(param_string); npos += strlen(param_string);
@ -912,38 +1093,27 @@ int nf;
} }
/* Change linefeed to carriage-return/linefeed */ /* Change linefeed to carriage-return/linefeed */
char * int
convert_linefeeds(char *si, char *dst, size_t max) convert_linefeeds(char *si, char *dst, size_t max)
{ {
size_t i = 0, out = 0; size_t i = 0, out = 0;
static char sout[TEXT_FIELD_SIZE+5];
char *p;
if (dst) for (i = 0; i < strlen(si) && out < max - 1; i++) {
p = dst;
else {
p = sout;
max = sizeof(sout);
}
p[0] = '\0';
for (i = 0; i < strlen(si) && out < max; i++) {
if (si[i] == '\n') { if (si[i] == '\n') {
/* Only add the carriage-return if needed */ /* Only add the carriage-return if needed */
if (i > 0 && si[i-1] == '\r') { if (i > 0 && si[i-1] == '\r') {
p[out++] = si[i]; dst[out++] = si[i];
continue; continue;
} }
p[out++] = '\r'; dst[out++] = '\r';
p[out++] = '\n'; dst[out++] = '\n';
} }
else else
p[out++] = si[i]; dst[out++] = si[i];
} }
p[out] = '\0'; dst[out] = '\0';
return p; return out;
} }
/* Change carriage-return/linefeed to just linefeed /* Change carriage-return/linefeed to just linefeed
@ -1030,6 +1200,7 @@ convert_from_pgbinary(unsigned char *value, unsigned char *rgbValue, int cbValue
size_t i; size_t i;
int o=0; int o=0;
for (i = 0; i < strlen(value); ) { for (i = 0; i < strlen(value); ) {
if (value[i] == '\\') { if (value[i] == '\\') {
rgbValue[o] = conv_from_octal(&value[i]); rgbValue[o] = conv_from_octal(&value[i]);
@ -1042,7 +1213,7 @@ int o=0;
o++; o++;
} }
rgbValue[o] = '\0'; rgbValue[o] = '\0'; // extra protection
return o; return o;
} }
@ -1075,12 +1246,13 @@ int i, o=0;
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
mylog("convert_to_pgbinary: in[%d] = %d, %c\n", i, in[i], in[i]); mylog("convert_to_pgbinary: in[%d] = %d, %c\n", i, in[i], in[i]);
if (in[i] < 32 || in[i] > 126) { if ( isalnum(in[i]) || in[i] == ' ') {
out[o++] = in[i];
}
else {
strcpy(&out[o], conv_to_octal(in[i])); strcpy(&out[o], conv_to_octal(in[i]));
o += 5; o += 5;
} }
else
out[o++] = in[i];
} }
@ -1150,15 +1322,25 @@ unsigned int i, o = 0;
*/ */
int int
convert_lo(StatementClass *stmt, void *value, Int2 fCType, PTR rgbValue, convert_lo(StatementClass *stmt, void *value, Int2 fCType, PTR rgbValue,
SDWORD cbValueMax, SDWORD *pcbValue, char multiple) SDWORD cbValueMax, SDWORD *pcbValue)
{ {
Oid oid; Oid oid;
int retval; int retval, result, left = -1;
int bind_row = stmt->bind_row;
BindInfoClass *bindInfo = NULL;
/* If using SQLGetData, then current_col will be set */
if (stmt->current_col >= 0) {
bindInfo = &stmt->bindings[stmt->current_col];
left = bindInfo->data_left;
}
/* if this is the first call for this column, /* if this is the first call for this column,
open the large object for reading open the large object for reading
*/ */
if ( ! multiple) {
if ( ! bindInfo || bindInfo->data_left == -1) {
oid = atoi(value); oid = atoi(value);
stmt->lobj_fd = lo_open(stmt->hdbc, oid, INV_READ); stmt->lobj_fd = lo_open(stmt->hdbc, oid, INV_READ);
if (stmt->lobj_fd < 0) { if (stmt->lobj_fd < 0) {
@ -1166,6 +1348,22 @@ int retval;
stmt->errormsg = "Couldnt open large object for reading."; stmt->errormsg = "Couldnt open large object for reading.";
return COPY_GENERAL_ERROR; return COPY_GENERAL_ERROR;
} }
/* Get the size */
retval = lo_lseek(stmt->hdbc, stmt->lobj_fd, 0L, SEEK_END);
if (retval >= 0) {
left = lo_tell(stmt->hdbc, stmt->lobj_fd);
if (bindInfo)
bindInfo->data_left = left;
/* return to beginning */
lo_lseek(stmt->hdbc, stmt->lobj_fd, 0L, SEEK_SET);
}
}
if (left == 0) {
return COPY_NO_DATA_FOUND;
} }
if (stmt->lobj_fd < 0) { if (stmt->lobj_fd < 0) {
@ -1174,7 +1372,7 @@ int retval;
return COPY_GENERAL_ERROR; return COPY_GENERAL_ERROR;
} }
retval = lo_read(stmt->hdbc, stmt->lobj_fd, rgbValue, cbValueMax); retval = lo_read(stmt->hdbc, stmt->lobj_fd, (char *) rgbValue, cbValueMax);
if (retval < 0) { if (retval < 0) {
lo_close(stmt->hdbc, stmt->lobj_fd); lo_close(stmt->hdbc, stmt->lobj_fd);
stmt->lobj_fd = -1; stmt->lobj_fd = -1;
@ -1183,20 +1381,26 @@ int retval;
stmt->errormsg = "Error reading from large object."; stmt->errormsg = "Error reading from large object.";
return COPY_GENERAL_ERROR; return COPY_GENERAL_ERROR;
} }
else if (retval < cbValueMax) { /* success, all done */
if (retval < left)
result = COPY_RESULT_TRUNCATED;
else
result = COPY_OK;
if (pcbValue)
*pcbValue = left < 0 ? SQL_NO_TOTAL : left;
if (bindInfo && bindInfo->data_left > 0)
bindInfo->data_left -= retval;
if (! bindInfo || bindInfo->data_left == 0) {
lo_close(stmt->hdbc, stmt->lobj_fd); lo_close(stmt->hdbc, stmt->lobj_fd);
stmt->lobj_fd = -1; /* prevent further reading */ stmt->lobj_fd = -1; /* prevent further reading */
if (pcbValue)
*pcbValue = retval;
return COPY_OK;
} }
else { /* retval == cbVaueMax -- assume truncated */
if (pcbValue)
*pcbValue = SQL_NO_TOTAL;
return COPY_RESULT_TRUNCATED;
} return result;
} }

View File

@ -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);
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

View File

@ -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,
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,
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];
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];
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);

View File

@ -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 @@
#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 */

View File

@ -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;
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;
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;
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:

View File

@ -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;
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;
// - - - - - - - - - // - - - - - - - - -
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;
} }
// 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

View File

@ -71,7 +71,7 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
ConnInfo *ci; ConnInfo *ci;
char *p; char *p;
mylog( "%s: entering...\n", func); mylog( "%s: entering...fInfoType=%d\n", func, fInfoType);
if ( ! conn) { if ( ! conn) {
CC_log_error(func, "", NULL); CC_log_error(func, "", NULL);
@ -224,7 +224,7 @@ char *p;
break; break;
case SQL_DBMS_NAME: /* ODBC 1.0 */ case SQL_DBMS_NAME: /* ODBC 1.0 */
if (pcbInfoValue) *pcbInfoValue = 10; if (pcbInfoValue) *pcbInfoValue = strlen(DBMS_NAME);
strncpy_null((char *)rgbInfoValue, DBMS_NAME, (size_t)cbInfoValueMax); strncpy_null((char *)rgbInfoValue, DBMS_NAME, (size_t)cbInfoValueMax);
break; break;
@ -238,7 +238,7 @@ char *p;
// by direct experimentation they are not. postgres forces // by direct experimentation they are not. postgres forces
// the newer transaction to wait before doing something that // the newer transaction to wait before doing something that
// would cause one of these problems. // would cause one of these problems.
*((DWORD *)rgbInfoValue) = SQL_TXN_SERIALIZABLE; *((DWORD *)rgbInfoValue) = SQL_TXN_READ_COMMITTED; //SQL_TXN_SERIALIZABLE;
if(pcbInfoValue) { *pcbInfoValue = 4; } if(pcbInfoValue) { *pcbInfoValue = 4; }
break; break;
@ -269,11 +269,12 @@ char *p;
case SQL_FETCH_DIRECTION: /* ODBC 1.0 */ case SQL_FETCH_DIRECTION: /* ODBC 1.0 */
// which fetch directions are supported? (bitmask) // which fetch directions are supported? (bitmask)
*((DWORD *)rgbInfoValue) = globals.use_declarefetch ? 0 : (SQL_FD_FETCH_NEXT | *((DWORD *)rgbInfoValue) = globals.use_declarefetch ? (SQL_FD_FETCH_NEXT) : (SQL_FD_FETCH_NEXT |
SQL_FD_FETCH_FIRST | SQL_FD_FETCH_FIRST |
SQL_FD_FETCH_LAST | SQL_FD_FETCH_LAST |
SQL_FD_FETCH_PRIOR | SQL_FD_FETCH_PRIOR |
SQL_FD_FETCH_ABSOLUTE); SQL_FD_FETCH_ABSOLUTE |
SQL_FD_FETCH_RELATIVE);
if(pcbInfoValue) { *pcbInfoValue = 4; } if(pcbInfoValue) { *pcbInfoValue = 4; }
break; break;
@ -285,7 +286,7 @@ char *p;
case SQL_GETDATA_EXTENSIONS: /* ODBC 2.0 */ case SQL_GETDATA_EXTENSIONS: /* ODBC 2.0 */
// (bitmask) // (bitmask)
*((DWORD *)rgbInfoValue) = (SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BOUND); *((DWORD *)rgbInfoValue) = (SQL_GD_ANY_COLUMN | SQL_GD_ANY_ORDER | SQL_GD_BOUND | SQL_GD_BLOCK);
if(pcbInfoValue) { *pcbInfoValue = 4; } if(pcbInfoValue) { *pcbInfoValue = 4; }
break; break;
@ -296,8 +297,9 @@ char *p;
break; break;
case SQL_IDENTIFIER_CASE: /* ODBC 1.0 */ case SQL_IDENTIFIER_CASE: /* ODBC 1.0 */
// are identifiers case-sensitive (yes) // are identifiers case-sensitive (yes, but only when quoted. If not quoted, they
*((WORD *)rgbInfoValue) = SQL_IC_SENSITIVE; // default to lowercase)
*((WORD *)rgbInfoValue) = SQL_IC_LOWER;
if(pcbInfoValue) { *pcbInfoValue = 2; } if(pcbInfoValue) { *pcbInfoValue = 2; }
break; break;
@ -324,8 +326,7 @@ char *p;
case SQL_LOCK_TYPES: /* ODBC 2.0 */ case SQL_LOCK_TYPES: /* ODBC 2.0 */
// which lock types does SQLSetPos support? (bitmask) // which lock types does SQLSetPos support? (bitmask)
// SQLSetPos doesn't exist yet *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_LCK_NO_CHANGE | SQL_LCK_EXCLUSIVE | SQL_LCK_UNLOCK) : SQL_LCK_NO_CHANGE;
*((DWORD *)rgbInfoValue) = globals.lie ? (SQL_LCK_NO_CHANGE | SQL_LCK_EXCLUSIVE | SQL_LCK_UNLOCK) : 0;
if(pcbInfoValue) { *pcbInfoValue = 4; } if(pcbInfoValue) { *pcbInfoValue = 4; }
break; break;
@ -531,8 +532,7 @@ char *p;
case SQL_POS_OPERATIONS: /* ODBC 2.0 */ case SQL_POS_OPERATIONS: /* ODBC 2.0 */
// what functions does SQLSetPos support? (bitmask) // what functions does SQLSetPos support? (bitmask)
// SQLSetPos does not exist yet *((DWORD *)rgbInfoValue) = globals.lie ? (SQL_POS_POSITION | SQL_POS_REFRESH | SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD) : (SQL_POS_POSITION | SQL_POS_REFRESH);
*((DWORD *)rgbInfoValue) = globals.lie ? (SQL_POS_POSITION | SQL_POS_REFRESH | SQL_POS_UPDATE | SQL_POS_DELETE | SQL_POS_ADD) : 0;
if(pcbInfoValue) { *pcbInfoValue = 4; } if(pcbInfoValue) { *pcbInfoValue = 4; }
break; break;
@ -585,8 +585,7 @@ char *p;
break; break;
case SQL_QUOTED_IDENTIFIER_CASE: /* ODBC 2.0 */ case SQL_QUOTED_IDENTIFIER_CASE: /* ODBC 2.0 */
// are "quoted" identifiers case-sensitive? // are "quoted" identifiers case-sensitive? YES
// well, we don't really let you quote identifiers, so...
*((WORD *)rgbInfoValue) = SQL_IC_SENSITIVE; *((WORD *)rgbInfoValue) = SQL_IC_SENSITIVE;
if(pcbInfoValue) { *pcbInfoValue = 2; } if(pcbInfoValue) { *pcbInfoValue = 2; }
break; break;
@ -713,7 +712,7 @@ char *p;
case SQL_TXN_ISOLATION_OPTION: /* ODBC 1.0 */ case SQL_TXN_ISOLATION_OPTION: /* ODBC 1.0 */
// what transaction isolation options are available? (bitmask) // what transaction isolation options are available? (bitmask)
// only the default--serializable transactions. // only the default--serializable transactions.
*((DWORD *)rgbInfoValue) = SQL_TXN_SERIALIZABLE; *((DWORD *)rgbInfoValue) = SQL_TXN_READ_COMMITTED; // SQL_TXN_SERIALIZABLE;
if(pcbInfoValue) { *pcbInfoValue = 4; } if(pcbInfoValue) { *pcbInfoValue = 4; }
break; break;
@ -751,7 +750,9 @@ static char *func = "SQLGetTypeInfo";
StatementClass *stmt = (StatementClass *) hstmt; StatementClass *stmt = (StatementClass *) hstmt;
TupleNode *row; TupleNode *row;
int i; int i;
Int4 type; // Int4 type;
Int4 pgType;
Int2 sqlType;
mylog("%s: entering...fSqlType = %d\n", func, fSqlType); mylog("%s: entering...fSqlType = %d\n", func, fSqlType);
@ -760,6 +761,7 @@ Int4 type;
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
} }
stmt->manual_result = TRUE; stmt->manual_result = TRUE;
stmt->result = QR_Constructor(); stmt->result = QR_Constructor();
if( ! stmt->result) { if( ! stmt->result) {
@ -786,25 +788,58 @@ Int4 type;
QR_set_field_info(stmt->result, 13, "MINIMUM_SCALE", PG_TYPE_INT2, 2); QR_set_field_info(stmt->result, 13, "MINIMUM_SCALE", PG_TYPE_INT2, 2);
QR_set_field_info(stmt->result, 14, "MAXIMUM_SCALE", PG_TYPE_INT2, 2); QR_set_field_info(stmt->result, 14, "MAXIMUM_SCALE", PG_TYPE_INT2, 2);
// cycle through the types for(i=0, sqlType = sqlTypes[0]; sqlType; sqlType = sqlTypes[++i]) {
for(i=0, type = pgtypes_defined[0]; type; type = pgtypes_defined[++i]) { pgType = sqltype_to_pgtype(sqlType);
if(fSqlType == SQL_ALL_TYPES || fSqlType == pgtype_to_sqltype(stmt, type)) {
if (fSqlType == SQL_ALL_TYPES || fSqlType == sqlType) {
row = (TupleNode *)malloc(sizeof(TupleNode) + (15 - 1)*sizeof(TupleField)); row = (TupleNode *)malloc(sizeof(TupleNode) + (15 - 1)*sizeof(TupleField));
/* These values can't be NULL */ /* These values can't be NULL */
set_tuplefield_string(&row->tuple[0], pgtype_to_name(stmt, pgType));
set_tuplefield_int2(&row->tuple[1], (Int2) sqlType);
set_tuplefield_int2(&row->tuple[6], pgtype_nullable(stmt, pgType));
set_tuplefield_int2(&row->tuple[7], pgtype_case_sensitive(stmt, pgType));
set_tuplefield_int2(&row->tuple[8], pgtype_searchable(stmt, pgType));
set_tuplefield_int2(&row->tuple[10], pgtype_money(stmt, pgType));
/* Localized data-source dependent data type name (always NULL) */
set_tuplefield_null(&row->tuple[12]);
/* These values can be NULL */
set_nullfield_int4(&row->tuple[2], pgtype_precision(stmt, pgType, PG_STATIC, PG_STATIC));
set_nullfield_string(&row->tuple[3], pgtype_literal_prefix(stmt, pgType));
set_nullfield_string(&row->tuple[4], pgtype_literal_suffix(stmt, pgType));
set_nullfield_string(&row->tuple[5], pgtype_create_params(stmt, pgType));
set_nullfield_int2(&row->tuple[9], pgtype_unsigned(stmt, pgType));
set_nullfield_int2(&row->tuple[11], pgtype_auto_increment(stmt, pgType));
set_nullfield_int2(&row->tuple[13], pgtype_scale(stmt, pgType));
set_nullfield_int2(&row->tuple[14], pgtype_scale(stmt, pgType));
QR_add_tuple(stmt->result, row);
}
}
// cycle through the types
// for(i=0, type = pgtypes_defined[0]; type; type = pgtypes_defined[++i]) {
// if(fSqlType == SQL_ALL_TYPES || fSqlType == pgtype_to_sqltype(stmt, type)) {
// row = (TupleNode *)malloc(sizeof(TupleNode) + (15 - 1)*sizeof(TupleField));
/* These values can't be NULL */
/*
set_tuplefield_string(&row->tuple[0], pgtype_to_name(stmt, type)); set_tuplefield_string(&row->tuple[0], pgtype_to_name(stmt, type));
set_tuplefield_int2(&row->tuple[1], pgtype_to_sqltype(stmt, type)); set_tuplefield_int2(&row->tuple[1], pgtype_to_sqltype(stmt, type));
set_tuplefield_int2(&row->tuple[6], pgtype_nullable(stmt, type)); set_tuplefield_int2(&row->tuple[6], pgtype_nullable(stmt, type));
set_tuplefield_int2(&row->tuple[7], pgtype_case_sensitive(stmt, type)); set_tuplefield_int2(&row->tuple[7], pgtype_case_sensitive(stmt, type));
set_tuplefield_int2(&row->tuple[8], pgtype_searchable(stmt, type)); set_tuplefield_int2(&row->tuple[8], pgtype_searchable(stmt, type));
set_tuplefield_int2(&row->tuple[10], pgtype_money(stmt, type)); set_tuplefield_int2(&row->tuple[10], pgtype_money(stmt, type));
*/
/* Localized data-source dependent data type name (always NULL) */ /* Localized data-source dependent data type name (always NULL) */
set_tuplefield_null(&row->tuple[12]); // set_tuplefield_null(&row->tuple[12]);
/* These values can be NULL */ /* These values can be NULL */
/*
set_nullfield_int4(&row->tuple[2], pgtype_precision(stmt, type, PG_STATIC, PG_STATIC)); set_nullfield_int4(&row->tuple[2], pgtype_precision(stmt, type, PG_STATIC, PG_STATIC));
set_nullfield_string(&row->tuple[3], pgtype_literal_prefix(stmt, type)); set_nullfield_string(&row->tuple[3], pgtype_literal_prefix(stmt, type));
set_nullfield_string(&row->tuple[4], pgtype_literal_suffix(stmt, type)); set_nullfield_string(&row->tuple[4], pgtype_literal_suffix(stmt, type));
@ -815,11 +850,13 @@ Int4 type;
set_nullfield_int2(&row->tuple[14], pgtype_scale(stmt, type)); set_nullfield_int2(&row->tuple[14], pgtype_scale(stmt, type));
QR_add_tuple(stmt->result, row); QR_add_tuple(stmt->result, row);
} */
} // }
// }
stmt->status = STMT_FINISHED; stmt->status = STMT_FINISHED;
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->rowset_start = -1;
stmt->current_col = -1; stmt->current_col = -1;
return SQL_SUCCESS; return SQL_SUCCESS;
@ -901,7 +938,7 @@ static char *func="SQLGetFunctions";
pfExists[SQL_API_SQLDATASOURCES] = FALSE; // only implemented by DM pfExists[SQL_API_SQLDATASOURCES] = FALSE; // only implemented by DM
pfExists[SQL_API_SQLDESCRIBEPARAM] = FALSE; // not properly implemented pfExists[SQL_API_SQLDESCRIBEPARAM] = FALSE; // not properly implemented
pfExists[SQL_API_SQLDRIVERS] = FALSE; // only implemented by DM pfExists[SQL_API_SQLDRIVERS] = FALSE; // only implemented by DM
pfExists[SQL_API_SQLEXTENDEDFETCH] = globals.use_declarefetch ? FALSE : TRUE; pfExists[SQL_API_SQLEXTENDEDFETCH] = TRUE;
pfExists[SQL_API_SQLFOREIGNKEYS] = TRUE; pfExists[SQL_API_SQLFOREIGNKEYS] = TRUE;
pfExists[SQL_API_SQLMORERESULTS] = TRUE; pfExists[SQL_API_SQLMORERESULTS] = TRUE;
pfExists[SQL_API_SQLNATIVESQL] = TRUE; pfExists[SQL_API_SQLNATIVESQL] = TRUE;
@ -910,8 +947,8 @@ static char *func="SQLGetFunctions";
pfExists[SQL_API_SQLPRIMARYKEYS] = TRUE; pfExists[SQL_API_SQLPRIMARYKEYS] = TRUE;
pfExists[SQL_API_SQLPROCEDURECOLUMNS] = FALSE; pfExists[SQL_API_SQLPROCEDURECOLUMNS] = FALSE;
pfExists[SQL_API_SQLPROCEDURES] = FALSE; pfExists[SQL_API_SQLPROCEDURES] = FALSE;
pfExists[SQL_API_SQLSETPOS] = FALSE; pfExists[SQL_API_SQLSETPOS] = TRUE;
pfExists[SQL_API_SQLSETSCROLLOPTIONS] = FALSE; // odbc 1.0 pfExists[SQL_API_SQLSETSCROLLOPTIONS] = TRUE; // odbc 1.0
pfExists[SQL_API_SQLTABLEPRIVILEGES] = FALSE; pfExists[SQL_API_SQLTABLEPRIVILEGES] = FALSE;
} }
} else { } else {
@ -970,7 +1007,7 @@ static char *func="SQLGetFunctions";
case SQL_API_SQLDATASOURCES: *pfExists = FALSE; break; // only implemented by DM case SQL_API_SQLDATASOURCES: *pfExists = FALSE; break; // only implemented by DM
case SQL_API_SQLDESCRIBEPARAM: *pfExists = FALSE; break; // not properly implemented case SQL_API_SQLDESCRIBEPARAM: *pfExists = FALSE; break; // not properly implemented
case SQL_API_SQLDRIVERS: *pfExists = FALSE; break; // only implemented by DM case SQL_API_SQLDRIVERS: *pfExists = FALSE; break; // only implemented by DM
case SQL_API_SQLEXTENDEDFETCH: *pfExists = globals.use_declarefetch ? FALSE : TRUE; break; case SQL_API_SQLEXTENDEDFETCH: *pfExists = TRUE; break;
case SQL_API_SQLFOREIGNKEYS: *pfExists = TRUE; break; case SQL_API_SQLFOREIGNKEYS: *pfExists = TRUE; break;
case SQL_API_SQLMORERESULTS: *pfExists = TRUE; break; case SQL_API_SQLMORERESULTS: *pfExists = TRUE; break;
case SQL_API_SQLNATIVESQL: *pfExists = TRUE; break; case SQL_API_SQLNATIVESQL: *pfExists = TRUE; break;
@ -979,8 +1016,8 @@ static char *func="SQLGetFunctions";
case SQL_API_SQLPRIMARYKEYS: *pfExists = TRUE; break; case SQL_API_SQLPRIMARYKEYS: *pfExists = TRUE; break;
case SQL_API_SQLPROCEDURECOLUMNS: *pfExists = FALSE; break; case SQL_API_SQLPROCEDURECOLUMNS: *pfExists = FALSE; break;
case SQL_API_SQLPROCEDURES: *pfExists = FALSE; break; case SQL_API_SQLPROCEDURES: *pfExists = FALSE; break;
case SQL_API_SQLSETPOS: *pfExists = FALSE; break; case SQL_API_SQLSETPOS: *pfExists = TRUE; break;
case SQL_API_SQLSETSCROLLOPTIONS: *pfExists = FALSE; break; // odbc 1.0 case SQL_API_SQLSETSCROLLOPTIONS: *pfExists = TRUE; break; // odbc 1.0
case SQL_API_SQLTABLEPRIVILEGES: *pfExists = FALSE; break; case SQL_API_SQLTABLEPRIVILEGES: *pfExists = FALSE; break;
} }
} }
@ -1253,6 +1290,7 @@ mylog("%s: entering...stmt=%u\n", func, stmt);
// set up the current tuple pointer for SQLFetch // set up the current tuple pointer for SQLFetch
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->rowset_start = -1;
stmt->current_col = -1; stmt->current_col = -1;
SQLFreeStmt(htbl_stmt, SQL_DROP); SQLFreeStmt(htbl_stmt, SQL_DROP);
@ -1600,6 +1638,7 @@ ConnInfo *ci;
// set up the current tuple pointer for SQLFetch // set up the current tuple pointer for SQLFetch
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->rowset_start = -1;
stmt->current_col = -1; stmt->current_col = -1;
SQLFreeStmt(hcol_stmt, SQL_DROP); SQLFreeStmt(hcol_stmt, SQL_DROP);
@ -1683,6 +1722,7 @@ mylog("%s: entering...stmt=%u\n", func, stmt);
} }
stmt->status = STMT_FINISHED; stmt->status = STMT_FINISHED;
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->rowset_start = -1;
stmt->current_col = -1; stmt->current_col = -1;
mylog("SQLSpecialColumns(): EXIT, stmt=%u\n", stmt); mylog("SQLSpecialColumns(): EXIT, stmt=%u\n", stmt);
@ -2002,6 +2042,7 @@ mylog("%s: entering...stmt=%u\n", func, stmt);
// set up the current tuple pointer for SQLFetch // set up the current tuple pointer for SQLFetch
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->rowset_start = -1;
stmt->current_col = -1; stmt->current_col = -1;
error = FALSE; error = FALSE;
@ -2186,6 +2227,7 @@ Int2 result_cols;
// set up the current tuple pointer for SQLFetch // set up the current tuple pointer for SQLFetch
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->rowset_start = -1;
stmt->current_col = -1; stmt->current_col = -1;
mylog("SQLPrimaryKeys(): EXIT, stmt=%u\n", stmt); mylog("SQLPrimaryKeys(): EXIT, stmt=%u\n", stmt);
@ -2270,6 +2312,7 @@ Int2 result_cols;
// set up the current tuple pointer for SQLFetch // set up the current tuple pointer for SQLFetch
stmt->currTuple = -1; stmt->currTuple = -1;
stmt->rowset_start = -1;
stmt->current_col = -1; stmt->current_col = -1;

View File

@ -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;
} }

View File

@ -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
#define MYLOGDIR "c:"
#endif
void mylog(); /* prototype */
#else #else
#define MYLOGDIR "c:" #ifndef WIN32
#endif #define mylog(args...) /* GNU convention for variable arguments */
void mylog(); /* prototype */ #else
#else #define mylog // mylog
#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
#define QLOGDIR "c:"
#endif
void qlog(); /* prototype */
#else #else
#define QLOGDIR "c:" #ifndef WIN32
#endif #define qlog(args...) /* GNU convention for variable arguments */
void qlog(); /* prototype */ #else
#else #define qlog // qlog
#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);

View File

@ -37,6 +37,219 @@
extern GLOBAL_VALUES globals; extern GLOBAL_VALUES globals;
RETCODE set_statement_option(ConnectionClass *conn,
StatementClass *stmt,
UWORD fOption,
UDWORD vParam)
{
static char *func="set_statement_option";
char changed = FALSE;
switch(fOption) {
case SQL_ASYNC_ENABLE:/* ignored */
break;
case SQL_BIND_TYPE:
/* now support multi-column and multi-row binding */
if (conn) conn->stmtOptions.bind_size = vParam;
if (stmt) stmt->options.bind_size = vParam;
break;
case SQL_CONCURRENCY:
/* positioned update isn't supported so cursor concurrency is read-only */
if (conn) conn->stmtOptions.scroll_concurrency = vParam;
if (stmt) stmt->options.scroll_concurrency = vParam;
break;
/*
if (globals.lie) {
if (conn) conn->stmtOptions.scroll_concurrency = vParam;
if (stmt) stmt->options.scroll_concurrency = vParam;
}
else {
if (conn) conn->stmtOptions.scroll_concurrency = SQL_CONCUR_READ_ONLY;
if (stmt) stmt->options.scroll_concurrency = SQL_CONCUR_READ_ONLY;
if (vParam != SQL_CONCUR_READ_ONLY)
changed = TRUE;
}
break;
*/
case SQL_CURSOR_TYPE:
/* if declare/fetch, then type can only be forward.
otherwise, it can only be forward or static.
*/
mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d\n", vParam);
if (globals.lie) {
if (conn) conn->stmtOptions.cursor_type = vParam;
if (stmt) stmt->options.cursor_type = vParam;
}
else {
if (globals.use_declarefetch) {
if (conn) conn->stmtOptions.cursor_type = SQL_CURSOR_FORWARD_ONLY;
if (stmt) stmt->options.cursor_type = SQL_CURSOR_FORWARD_ONLY;
if (vParam != SQL_CURSOR_FORWARD_ONLY)
changed = TRUE;
}
else {
if (vParam == SQL_CURSOR_FORWARD_ONLY || vParam == SQL_CURSOR_STATIC) {
if (conn) conn->stmtOptions.cursor_type = vParam; // valid type
if (stmt) stmt->options.cursor_type = vParam; // valid type
}
else {
if (conn) conn->stmtOptions.cursor_type = SQL_CURSOR_STATIC;
if (stmt) stmt->options.cursor_type = SQL_CURSOR_STATIC;
changed = TRUE;
}
}
}
break;
case SQL_KEYSET_SIZE: /* ignored, but saved and returned */
mylog("SetStmtOption(): SQL_KEYSET_SIZE, vParam = %d\n", vParam);
if (conn) conn->stmtOptions.keyset_size = vParam;
if (stmt) stmt->options.keyset_size = vParam;
break;
/*
if (globals.lie)
stmt->keyset_size = vParam;
else {
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Driver does not support keyset size option";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
*/
case SQL_MAX_LENGTH:/* ignored, but saved */
mylog("SetStmtOption(): SQL_MAX_LENGTH, vParam = %d\n", vParam);
if (conn) conn->stmtOptions.maxLength = vParam;
if (stmt) stmt->options.maxLength = vParam;
break;
case SQL_MAX_ROWS: /* ignored, but saved */
mylog("SetStmtOption(): SQL_MAX_ROWS, vParam = %d\n", vParam);
if (conn) conn->stmtOptions.maxRows = vParam;
if (stmt) stmt->options.maxRows = vParam;
break;
case SQL_NOSCAN: /* ignored */
mylog("SetStmtOption: SQL_NOSCAN, vParam = %d\n", vParam);
break;
case SQL_QUERY_TIMEOUT: /* ignored */
mylog("SetStmtOption: SQL_QUERY_TIMEOUT, vParam = %d\n", vParam);
// "0" returned in SQLGetStmtOption
break;
case SQL_RETRIEVE_DATA: /* ignored, but saved */
mylog("SetStmtOption(): SQL_RETRIEVE_DATA, vParam = %d\n", vParam);
if (conn) conn->stmtOptions.retrieve_data = vParam;
if (stmt) stmt->options.retrieve_data = vParam;
break;
case SQL_ROWSET_SIZE:
mylog("SetStmtOption(): SQL_ROWSET_SIZE, vParam = %d\n", vParam);
/* Save old rowset size for SQLExtendedFetch purposes
If the rowset_size is being changed since the last call
to fetch rows.
*/
if (stmt && stmt->save_rowset_size <= 0 && stmt->last_fetch_count > 0 )
stmt->save_rowset_size = stmt->options.rowset_size;
if (vParam < 1) {
vParam = 1;
changed = TRUE;
}
if (conn) conn->stmtOptions.rowset_size = vParam;
if (stmt) stmt->options.rowset_size = vParam;
break;
case SQL_SIMULATE_CURSOR: /* NOT SUPPORTED */
if (stmt) {
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Simulated positioned update/delete not supported. Use the cursor library.";
SC_log_error(func, "", stmt);
}
if (conn) {
conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
conn->errormsg = "Simulated positioned update/delete not supported. Use the cursor library.";
CC_log_error(func, "", conn);
}
return SQL_ERROR;
case SQL_USE_BOOKMARKS: /* NOT SUPPORTED */
if (stmt) {
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Driver does not support (SET) using bookmarks.";
SC_log_error(func, "", stmt);
}
if (conn) {
conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
conn->errormsg = "Driver does not support (SET) using bookmarks.";
CC_log_error(func, "", conn);
}
return SQL_ERROR;
default:
{
char option[64];
if (stmt) {
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Unknown statement option (Set)";
sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
SC_log_error(func, option, stmt);
}
if (conn) {
conn->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
conn->errormsg = "Unknown statement option (Set)";
sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
CC_log_error(func, option, conn);
}
return SQL_ERROR;
}
}
if (changed) {
if (stmt) {
stmt->errormsg = "Requested value changed.";
stmt->errornumber = STMT_OPTION_VALUE_CHANGED;
}
if (conn) {
conn->errormsg = "Requested value changed.";
conn->errornumber = STMT_OPTION_VALUE_CHANGED;
}
return SQL_SUCCESS_WITH_INFO;
}
else
return SQL_SUCCESS;
}
/* Implements only SQL_AUTOCOMMIT */ /* Implements only SQL_AUTOCOMMIT */
RETCODE SQL_API SQLSetConnectOption( RETCODE SQL_API SQLSetConnectOption(
HDBC hdbc, HDBC hdbc,
@ -45,6 +258,9 @@ RETCODE SQL_API SQLSetConnectOption(
{ {
static char *func="SQLSetConnectOption"; static char *func="SQLSetConnectOption";
ConnectionClass *conn = (ConnectionClass *) hdbc; ConnectionClass *conn = (ConnectionClass *) hdbc;
char changed = FALSE;
RETCODE retval;
int i;
mylog("%s: entering...\n", func); mylog("%s: entering...\n", func);
@ -55,6 +271,47 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
switch (fOption) { switch (fOption) {
/* Statement Options --
(apply to all stmts on the connection and become defaults for new stmts)
*/
case SQL_ASYNC_ENABLE:
case SQL_BIND_TYPE:
case SQL_CONCURRENCY:
case SQL_CURSOR_TYPE:
case SQL_KEYSET_SIZE:
case SQL_MAX_LENGTH:
case SQL_MAX_ROWS:
case SQL_NOSCAN:
case SQL_QUERY_TIMEOUT:
case SQL_RETRIEVE_DATA:
case SQL_ROWSET_SIZE:
case SQL_SIMULATE_CURSOR:
case SQL_USE_BOOKMARKS:
/* Affect all current Statements */
for (i = 0; i < conn->num_stmts; i++) {
if ( conn->stmts[i]) {
set_statement_option(NULL, conn->stmts[i], fOption, vParam);
}
}
/* Become the default for all future statements on this connection */
retval = set_statement_option(conn, NULL, fOption, vParam);
if (retval == SQL_SUCCESS_WITH_INFO)
changed = TRUE;
else if (retval == SQL_ERROR)
return SQL_ERROR;
break;
/**********************************/
/***** Connection Options *******/
/**********************************/
case SQL_ACCESS_MODE: /* ignored */
break;
case SQL_AUTOCOMMIT: case SQL_AUTOCOMMIT:
/* Since we are almost always in a transaction, this is now ok. /* Since we are almost always in a transaction, this is now ok.
@ -89,19 +346,34 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
break; break;
case SQL_LOGIN_TIMEOUT: case SQL_CURRENT_QUALIFIER: /* ignored */
break; break;
case SQL_ACCESS_MODE: case SQL_LOGIN_TIMEOUT: /* ignored */
break; break;
case SQL_TXN_ISOLATION: case SQL_PACKET_SIZE: /* ignored */
break;
case SQL_QUIET_MODE: /* ignored */
break;
case SQL_TXN_ISOLATION: /* ignored */
break;
/* These options should be handled by driver manager */
case SQL_ODBC_CURSORS:
case SQL_OPT_TRACE:
case SQL_OPT_TRACEFILE:
case SQL_TRANSLATE_DLL:
case SQL_TRANSLATE_OPTION:
CC_log_error(func, "This connect option (Set) is only used by the Driver Manager", conn);
break; break;
default: default:
{ {
char option[64]; char option[64];
conn->errormsg = "Driver does not support setting this connect option"; conn->errormsg = "Unknown connect option (Set)";
conn->errornumber = CONN_UNSUPPORTED_OPTION; conn->errornumber = CONN_UNSUPPORTED_OPTION;
sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam); sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
CC_log_error(func, option, conn); CC_log_error(func, option, conn);
@ -109,7 +381,14 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
} }
} }
return SQL_SUCCESS;
if (changed) {
conn->errornumber = CONN_OPTION_VALUE_CHANGED;
conn->errormsg = "Requested value changed.";
return SQL_SUCCESS_WITH_INFO;
}
else
return SQL_SUCCESS;
} }
// - - - - - - - - - // - - - - - - - - -
@ -131,22 +410,50 @@ ConnectionClass *conn = (ConnectionClass *) hdbc;
} }
switch (fOption) { switch (fOption) {
case SQL_ACCESS_MODE:/* NOT SUPPORTED */
*((UDWORD *) pvParam) = SQL_MODE_READ_WRITE;
break;
case SQL_AUTOCOMMIT: case SQL_AUTOCOMMIT:
*((UDWORD *)pvParam) = (UDWORD)( CC_is_in_autocommit(conn) ? *((UDWORD *)pvParam) = (UDWORD)( CC_is_in_autocommit(conn) ?
SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF); SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF);
break; break;
/* don't use qualifiers */ case SQL_CURRENT_QUALIFIER: /* don't use qualifiers */
case SQL_CURRENT_QUALIFIER:
if(pvParam) if(pvParam)
strcpy(pvParam, ""); strcpy(pvParam, "");
break; break;
case SQL_LOGIN_TIMEOUT: /* NOT SUPPORTED */
*((UDWORD *) pvParam) = 0;
break;
case SQL_PACKET_SIZE: /* NOT SUPPORTED */
*((UDWORD *) pvParam) = globals.socket_buffersize;
break;
case SQL_QUIET_MODE:/* NOT SUPPORTED */
*((UDWORD *) pvParam) = (UDWORD) NULL;
break;
case SQL_TXN_ISOLATION:/* NOT SUPPORTED */
*((UDWORD *) pvParam) = SQL_TXN_SERIALIZABLE;
break;
/* These options should be handled by driver manager */
case SQL_ODBC_CURSORS:
case SQL_OPT_TRACE:
case SQL_OPT_TRACEFILE:
case SQL_TRANSLATE_DLL:
case SQL_TRANSLATE_OPTION:
CC_log_error(func, "This connect option (Get) is only used by the Driver Manager", conn);
break;
default: default:
{ {
char option[64]; char option[64];
conn->errormsg = "Driver does not support getting this connect option"; conn->errormsg = "Unknown connect option (Get)";
conn->errornumber = CONN_UNSUPPORTED_OPTION; conn->errornumber = CONN_UNSUPPORTED_OPTION;
sprintf(option, "fOption=%d", fOption); sprintf(option, "fOption=%d", fOption);
CC_log_error(func, option, conn); CC_log_error(func, option, conn);
@ -181,104 +488,7 @@ char changed = FALSE;
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
} }
switch(fOption) { return set_statement_option(NULL, stmt, fOption, vParam);
case SQL_QUERY_TIMEOUT:
mylog("SetStmtOption: vParam = %d\n", vParam);
// "0" returned in SQLGetStmtOption
break;
case SQL_MAX_LENGTH:
// "4096" returned in SQLGetStmtOption
break;
case SQL_MAX_ROWS:
mylog("SetStmtOption(): SQL_MAX_ROWS = %d, returning success\n", vParam);
stmt->maxRows = vParam;
return SQL_SUCCESS;
break;
case SQL_ROWSET_SIZE:
mylog("SetStmtOption(): SQL_ROWSET_SIZE = %d\n", vParam);
stmt->rowset_size = 1; // only support 1 row at a time
if (vParam != 1)
changed = TRUE;
break;
case SQL_KEYSET_SIZE:
mylog("SetStmtOption(): SQL_KEYSET_SIZE = %d\n", vParam);
if (globals.lie)
stmt->keyset_size = vParam;
else {
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Driver does not support keyset size option";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
break;
case SQL_CONCURRENCY:
// positioned update isn't supported so cursor concurrency is read-only
mylog("SetStmtOption(): SQL_CONCURRENCY = %d\n", vParam);
if (globals.lie)
stmt->scroll_concurrency = vParam;
else {
stmt->scroll_concurrency = SQL_CONCUR_READ_ONLY;
if (vParam != SQL_CONCUR_READ_ONLY)
changed = TRUE;
}
break;
case SQL_CURSOR_TYPE:
// if declare/fetch, then type can only be forward.
// otherwise, it can only be forward or static.
mylog("SetStmtOption(): SQL_CURSOR_TYPE = %d\n", vParam);
if (globals.lie)
stmt->cursor_type = vParam;
else {
if (globals.use_declarefetch) {
stmt->cursor_type = SQL_CURSOR_FORWARD_ONLY;
if (vParam != SQL_CURSOR_FORWARD_ONLY)
changed = TRUE;
}
else {
if (vParam == SQL_CURSOR_FORWARD_ONLY || vParam == SQL_CURSOR_STATIC)
stmt->cursor_type = vParam; // valid type
else {
stmt->cursor_type = SQL_CURSOR_STATIC;
changed = TRUE;
}
}
}
break;
case SQL_SIMULATE_CURSOR:
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Simulated positioned update/delete not supported. Use the cursor library.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
default:
{
char option[64];
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Driver does not support setting this statement option";
sprintf(option, "fOption=%d, vParam=%ld", fOption, vParam);
SC_log_error(func, option, stmt);
return SQL_ERROR;
}
}
if (changed) {
stmt->errormsg = "Requested value changed.";
stmt->errornumber = STMT_OPTION_VALUE_CHANGED;
return SQL_SUCCESS_WITH_INFO;
}
else
return SQL_SUCCESS;
} }
@ -304,54 +514,78 @@ StatementClass *stmt = (StatementClass *) hstmt;
} }
switch(fOption) { switch(fOption) {
case SQL_QUERY_TIMEOUT: case SQL_GET_BOOKMARK:/* NOT SUPPORTED */
// how long we wait on a query before returning to the stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
// application (0 == forever) stmt->errormsg = "Driver does not support getting bookmarks.";
*((SDWORD *)pvParam) = 0; SC_log_error(func, "", stmt);
return SQL_ERROR;
break; break;
case SQL_MAX_LENGTH: case SQL_ROW_NUMBER:
// what is the maximum length that will be returned in *((SDWORD *) pvParam) = stmt->currTuple + 1;
// a single column
*((SDWORD *)pvParam) = 4096;
break; break;
case SQL_MAX_ROWS: case SQL_ASYNC_ENABLE: /* NOT SUPPORTED */
*((SDWORD *)pvParam) = stmt->maxRows; *((SDWORD *) pvParam) = SQL_ASYNC_ENABLE_OFF;
mylog("GetSmtOption: MAX_ROWS, returning %d\n", stmt->maxRows); break;
case SQL_BIND_TYPE:
*((SDWORD *) pvParam) = stmt->options.bind_size;
break;
case SQL_CONCURRENCY: /* NOT REALLY SUPPORTED */
mylog("GetStmtOption(): SQL_CONCURRENCY\n");
*((SDWORD *)pvParam) = stmt->options.scroll_concurrency;
break;
case SQL_CURSOR_TYPE: /* PARTIAL SUPPORT */
mylog("GetStmtOption(): SQL_CURSOR_TYPE\n");
*((SDWORD *)pvParam) = stmt->options.cursor_type;
break;
case SQL_KEYSET_SIZE: /* NOT SUPPORTED, but saved */
mylog("GetStmtOption(): SQL_KEYSET_SIZE\n");
*((SDWORD *)pvParam) = stmt->options.keyset_size;
break;
case SQL_MAX_LENGTH: /* NOT SUPPORTED, but saved */
*((SDWORD *)pvParam) = stmt->options.maxLength;
break;
case SQL_MAX_ROWS: /* NOT SUPPORTED, but saved */
*((SDWORD *)pvParam) = stmt->options.maxRows;
mylog("GetSmtOption: MAX_ROWS, returning %d\n", stmt->options.maxRows);
break;
case SQL_NOSCAN:/* NOT SUPPORTED */
*((SDWORD *) pvParam) = SQL_NOSCAN_ON;
break;
case SQL_QUERY_TIMEOUT: /* NOT SUPPORTED */
*((SDWORD *) pvParam) = 0;
break;
case SQL_RETRIEVE_DATA: /* NOT SUPPORTED, but saved */
*((SDWORD *) pvParam) = stmt->options.retrieve_data;
break; break;
case SQL_ROWSET_SIZE: case SQL_ROWSET_SIZE:
mylog("GetStmtOption(): SQL_ROWSET_SIZE\n"); *((SDWORD *) pvParam) = stmt->options.rowset_size;
*((SDWORD *)pvParam) = stmt->rowset_size;
break; break;
case SQL_KEYSET_SIZE: case SQL_SIMULATE_CURSOR:/* NOT SUPPORTED */
mylog("GetStmtOption(): SQL_KEYSET_SIZE\n"); *((SDWORD *) pvParam) = SQL_SC_NON_UNIQUE;
*((SDWORD *)pvParam) = stmt->keyset_size;
break; break;
case SQL_CONCURRENCY: case SQL_USE_BOOKMARKS:/* NOT SUPPORTED */
mylog("GetStmtOption(): SQL_CONCURRENCY\n"); *((SDWORD *) pvParam) = SQL_UB_OFF;
*((SDWORD *)pvParam) = stmt->scroll_concurrency;
break; break;
case SQL_CURSOR_TYPE:
mylog("GetStmtOption(): SQL_CURSOR_TYPE\n");
*((SDWORD *)pvParam) = stmt->cursor_type;
break;
case SQL_SIMULATE_CURSOR:
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Simulated positioned update/delete not supported. Use the cursor library.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
default: default:
{ {
char option[64]; char option[64];
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR; stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Driver does not support getting this statement option"; stmt->errormsg = "Unknown statement option (Get)";
sprintf(option, "fOption=%d", fOption); sprintf(option, "fOption=%d", fOption);
SC_log_error(func, option, stmt); SC_log_error(func, option, stmt);
return SQL_ERROR; return SQL_ERROR;

View File

@ -74,6 +74,103 @@ Int4 pgtypes_defined[] = {
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)
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)
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";

View File

@ -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);

View File

@ -126,4 +126,3 @@ RETCODE SQL_API SQLDummyOrdinal(void)
return SQL_SUCCESS; return SQL_SUCCESS;
} }

View File

@ -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;
/* 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_
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"

View File

@ -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
// //
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
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"

View File

@ -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;
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;
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)
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);
@ -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)
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;
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;
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;
/* 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
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
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
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 */
mylog("next_tuple: sending actual fetch (%d) query '%s'\n", globals.fetch_max, fetch); /* 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", 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
*/ */
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
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
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;
} else } else
bmp <<= 1; bmp <<= 1;
} }
self->currTuple++;
return TRUE; return TRUE;
} }

View File

@ -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_ {
#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);
@ -105,4 +110,9 @@ 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

View File

@ -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

View File

@ -8,7 +8,7 @@
* *
* API functions: SQLRowCount, SQLNumResultCols, SQLDescribeCol, SQLColAttributes, * API functions: SQLRowCount, SQLNumResultCols, SQLDescribeCol, SQLColAttributes,
* SQLGetData, SQLFetch, SQLExtendedFetch, * SQLGetData, SQLFetch, SQLExtendedFetch,
* SQLMoreResults(NI), SQLSetPos(NI), SQLSetScrollOptions(NI), * SQLMoreResults(NI), SQLSetPos, SQLSetScrollOptions(NI),
* SQLSetCursorName, SQLGetCursorName * SQLSetCursorName, SQLGetCursorName
* *
* Comments: See "notice.txt" for copyright and license information. * Comments: See "notice.txt" for copyright and license information.
@ -57,13 +57,18 @@ char *msg, *ptr;
SC_log_error(func, "", NULL); SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
} }
if (stmt->manual_result) {
if (pcrow)
*pcrow = -1;
return SQL_SUCCESS;
}
if(stmt->statement_type == STMT_TYPE_SELECT) { if(stmt->statement_type == STMT_TYPE_SELECT) {
if (stmt->status == STMT_FINISHED) { if (stmt->status == STMT_FINISHED) {
res = SC_get_Result(stmt); res = SC_get_Result(stmt);
if(res && pcrow) { if(res && pcrow) {
*pcrow = globals.use_declarefetch ? 0 : QR_get_num_tuples(res); *pcrow = globals.use_declarefetch ? -1 : QR_get_num_tuples(res);
return SQL_SUCCESS; return SQL_SUCCESS;
} }
} }
@ -176,6 +181,7 @@ Int4 fieldtype = 0;
int precision = 0; int precision = 0;
ConnInfo *ci; ConnInfo *ci;
char parse_ok; char parse_ok;
char buf[255];
mylog("%s: entering...\n", func); mylog("%s: entering...\n", func);
@ -247,7 +253,8 @@ char parse_ok;
if (icol >= QR_NumResultCols(result)) { if (icol >= QR_NumResultCols(result)) {
stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR; stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
stmt->errormsg = "Invalid column number in DescribeCol."; stmt->errormsg = "Invalid column number in DescribeCol.";
SC_log_error(func, "", stmt); sprintf(buf, "Col#=%d, #Cols=%d", icol, QR_NumResultCols(result));
SC_log_error(func, buf, stmt);
return SQL_ERROR; return SQL_ERROR;
} }
@ -610,7 +617,6 @@ int num_cols, num_rows;
Int4 field_type; Int4 field_type;
void *value; void *value;
int result; int result;
char multiple;
mylog("SQLGetData: enter, stmt=%u\n", stmt); mylog("SQLGetData: enter, stmt=%u\n", stmt);
@ -674,7 +680,7 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
mylog(" value = '%s'\n", value); mylog(" value = '%s'\n", value);
} }
else { /* its a SOCKET result (backend data) */ else { /* its a SOCKET result (backend data) */
if (stmt->currTuple == -1 || ! res || QR_end_tuples(res)) { if (stmt->currTuple == -1 || ! res || ! res->tupleField) {
stmt->errormsg = "Not positioned on a valid row for GetData."; stmt->errormsg = "Not positioned on a valid row for GetData.";
stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR; stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
SC_log_error(func, "", stmt); SC_log_error(func, "", stmt);
@ -690,14 +696,12 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
mylog("**** SQLGetData: icol = %d, fCType = %d, field_type = %d, value = '%s'\n", icol, fCType, field_type, value); mylog("**** SQLGetData: icol = %d, fCType = %d, field_type = %d, value = '%s'\n", icol, fCType, field_type, value);
/* Is this another call for the same column to retrieve more data? */ stmt->current_col = icol;
multiple = (icol == stmt->current_col) ? TRUE : FALSE;
result = copy_and_convert_field(stmt, field_type, value, result = copy_and_convert_field(stmt, field_type, value,
fCType, rgbValue, cbValueMax, pcbValue, multiple); fCType, rgbValue, cbValueMax, pcbValue);
stmt->current_col = -1;
stmt->current_col = icol;
switch(result) { switch(result) {
case COPY_OK: case COPY_OK:
@ -725,7 +729,7 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
return SQL_ERROR; return SQL_ERROR;
case COPY_NO_DATA_FOUND: case COPY_NO_DATA_FOUND:
SC_log_error(func, "no data found", stmt); /* SC_log_error(func, "no data found", stmt); */
return SQL_NO_DATA_FOUND; return SQL_NO_DATA_FOUND;
default: default:
@ -736,70 +740,27 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt);
} }
} }
// Returns data for bound columns in the current row ("hstmt->iCursor"), RETCODE
// advances the cursor. SC_fetch(StatementClass *stmt)
RETCODE SQL_API SQLFetch(
HSTMT hstmt)
{ {
static char *func = "SQLFetch"; static char *func = "SC_fetch";
StatementClass *stmt = (StatementClass *) hstmt; QResultClass *res = stmt->result;
QResultClass *res; int retval, result;
int retval;
Int2 num_cols, lf; Int2 num_cols, lf;
Oid type; Oid type;
char *value; char *value;
ColumnInfoClass *ci; ColumnInfoClass *ci;
// TupleField *tupleField; // TupleField *tupleField;
mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result); stmt->last_fetch_count = 0;
if ( ! stmt) {
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
SC_clear_error(stmt);
if ( ! (res = stmt->result)) {
stmt->errormsg = "Null statement result in SQLFetch.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
ci = QR_get_fields(res); /* the column info */ ci = QR_get_fields(res); /* the column info */
if (stmt->status == STMT_EXECUTING) {
stmt->errormsg = "Can't fetch while statement is still executing.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->status != STMT_FINISHED) {
stmt->errornumber = STMT_STATUS_ERROR;
stmt->errormsg = "Fetch can only be called after the successful execution on a SQL statement";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->bindings == NULL) {
// just to avoid a crash if the user insists on calling this
// function even if SQL_ExecDirect has reported an Error
stmt->errormsg = "Bindings were not allocated properly.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
mylog("manual_result = %d, use_declarefetch = %d\n", stmt->manual_result, globals.use_declarefetch); mylog("manual_result = %d, use_declarefetch = %d\n", stmt->manual_result, globals.use_declarefetch);
if ( stmt->manual_result || ! globals.use_declarefetch) { if ( stmt->manual_result || ! globals.use_declarefetch) {
if (stmt->currTuple >= QR_get_num_tuples(res) -1 || if (stmt->currTuple >= QR_get_num_tuples(res) -1 ||
(stmt->maxRows > 0 && stmt->currTuple == stmt->maxRows - 1)) { (stmt->options.maxRows > 0 && stmt->currTuple == stmt->options.maxRows - 1)) {
/* if at the end of the tuples, return "no data found" /* if at the end of the tuples, return "no data found"
and set the cursor past the end of the result set and set the cursor past the end of the result set
@ -833,10 +794,16 @@ mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
num_cols = QR_NumResultCols(res); num_cols = QR_NumResultCols(res);
result = SQL_SUCCESS;
stmt->last_fetch_count = 1;
for (lf=0; lf < num_cols; lf++) { for (lf=0; lf < num_cols; lf++) {
mylog("fetch: cols=%d, lf=%d, stmt = %u, stmt->bindings = %u, buffer[] = %u\n", num_cols, lf, stmt, stmt->bindings, stmt->bindings[lf].buffer); mylog("fetch: cols=%d, lf=%d, stmt = %u, stmt->bindings = %u, buffer[] = %u\n", num_cols, lf, stmt, stmt->bindings, stmt->bindings[lf].buffer);
/* reset for SQLGetData */
stmt->bindings[lf].data_left = -1;
if (stmt->bindings[lf].buffer != NULL) { if (stmt->bindings[lf].buffer != NULL) {
// this column has a binding // this column has a binding
@ -855,13 +822,12 @@ mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
value = QR_get_value_backend_row(res, stmt->currTuple, lf); value = QR_get_value_backend_row(res, stmt->currTuple, lf);
} }
mylog("value = '%s'\n", value); mylog("value = '%s'\n", (value==NULL)?"<NULL>":value);
retval = copy_and_convert_field_bindinfo(stmt, type, value, lf); retval = copy_and_convert_field_bindinfo(stmt, type, value, lf);
mylog("copy_and_convert: retval = %d\n", retval); mylog("copy_and_convert: retval = %d\n", retval);
switch(retval) { switch(retval) {
case COPY_OK: case COPY_OK:
break; /* OK, do next bound column */ break; /* OK, do next bound column */
@ -870,37 +836,99 @@ mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
stmt->errormsg = "Received an unsupported type from Postgres."; stmt->errormsg = "Received an unsupported type from Postgres.";
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR; stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
SC_log_error(func, "", stmt); SC_log_error(func, "", stmt);
return SQL_ERROR; result = SQL_ERROR;
break;
case COPY_UNSUPPORTED_CONVERSION: case COPY_UNSUPPORTED_CONVERSION:
stmt->errormsg = "Couldn't handle the necessary data type conversion."; stmt->errormsg = "Couldn't handle the necessary data type conversion.";
stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR; stmt->errornumber = STMT_RESTRICTED_DATA_TYPE_ERROR;
SC_log_error(func, "", stmt); SC_log_error(func, "", stmt);
return SQL_ERROR; result = SQL_ERROR;
break;
case COPY_RESULT_TRUNCATED: case COPY_RESULT_TRUNCATED:
stmt->errornumber = STMT_TRUNCATED; stmt->errornumber = STMT_TRUNCATED;
stmt->errormsg = "The buffer was too small for the result."; stmt->errormsg = "The buffer was too small for the result.";
return SQL_SUCCESS_WITH_INFO; result = SQL_SUCCESS_WITH_INFO;
break;
case COPY_GENERAL_ERROR: /* error msg already filled in */ case COPY_GENERAL_ERROR: /* error msg already filled in */
SC_log_error(func, "", stmt); SC_log_error(func, "", stmt);
return SQL_ERROR; result = SQL_ERROR;
break;
/* This would not be meaningful in SQLFetch. */
case COPY_NO_DATA_FOUND: case COPY_NO_DATA_FOUND:
SC_log_error(func, "no data found", stmt); break;
return SQL_NO_DATA_FOUND;
default: default:
stmt->errormsg = "Unrecognized return value from copy_and_convert_field."; stmt->errormsg = "Unrecognized return value from copy_and_convert_field.";
stmt->errornumber = STMT_INTERNAL_ERROR; stmt->errornumber = STMT_INTERNAL_ERROR;
SC_log_error(func, "", stmt); SC_log_error(func, "", stmt);
return SQL_ERROR; result = SQL_ERROR;
break;
} }
} }
} }
return SQL_SUCCESS; return result;
}
// Returns data for bound columns in the current row ("hstmt->iCursor"),
// advances the cursor.
RETCODE SQL_API SQLFetch(
HSTMT hstmt)
{
static char *func = "SQLFetch";
StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result);
if ( ! stmt) {
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
SC_clear_error(stmt);
if ( ! (res = stmt->result)) {
stmt->errormsg = "Null statement result in SQLFetch.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->status == STMT_EXECUTING) {
stmt->errormsg = "Can't fetch while statement is still executing.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->status != STMT_FINISHED) {
stmt->errornumber = STMT_STATUS_ERROR;
stmt->errormsg = "Fetch can only be called after the successful execution on a SQL statement";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->bindings == NULL) {
// just to avoid a crash if the user insists on calling this
// function even if SQL_ExecDirect has reported an Error
stmt->errormsg = "Bindings were not allocated properly.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
QR_set_rowset_size(res, 1);
QR_inc_base(res, stmt->last_fetch_count);
return SC_fetch(stmt);
} }
// This fetchs a block of data (rowset). // This fetchs a block of data (rowset).
@ -914,9 +942,10 @@ RETCODE SQL_API SQLExtendedFetch(
{ {
static char *func = "SQLExtendedFetch"; static char *func = "SQLExtendedFetch";
StatementClass *stmt = (StatementClass *) hstmt; StatementClass *stmt = (StatementClass *) hstmt;
int num_tuples; QResultClass *res;
int num_tuples, i, save_rowset_size;
RETCODE result; RETCODE result;
char truncated, error;
mylog("SQLExtendedFetch: stmt=%u\n", stmt); mylog("SQLExtendedFetch: stmt=%u\n", stmt);
@ -925,43 +954,108 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt);
return SQL_INVALID_HANDLE; return SQL_INVALID_HANDLE;
} }
if ( globals.use_declarefetch) { if ( globals.use_declarefetch && ! stmt->manual_result) {
SC_log_error(func, "SQLExtendedFetch with UseDeclareFetch not yet supported", stmt); if ( fFetchType != SQL_FETCH_NEXT) {
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Unsupported fetch type for SQLExtendedFetch with UseDeclareFetch option.";
return SQL_ERROR;
}
}
SC_clear_error(stmt);
if ( ! (res = stmt->result)) {
stmt->errormsg = "Null statement result in SQLExtendedFetch.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->status == STMT_EXECUTING) {
stmt->errormsg = "Can't fetch while statement is still executing.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->status != STMT_FINISHED) {
stmt->errornumber = STMT_STATUS_ERROR;
stmt->errormsg = "ExtendedFetch can only be called after the successful execution on a SQL statement";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (stmt->bindings == NULL) {
// just to avoid a crash if the user insists on calling this
// function even if SQL_ExecDirect has reported an Error
stmt->errormsg = "Bindings were not allocated properly.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR; return SQL_ERROR;
} }
/* Initialize to no rows fetched */ /* Initialize to no rows fetched */
if (rgfRowStatus) if (rgfRowStatus)
*rgfRowStatus = SQL_ROW_NOROW; for (i = 0; i < stmt->options.rowset_size; i++)
*(rgfRowStatus + i) = SQL_ROW_NOROW;
if (pcrow) if (pcrow)
*pcrow = 0; *pcrow = 0;
num_tuples = QR_get_num_tuples(stmt->result); num_tuples = QR_get_num_tuples(res);
/* Save and discard the saved rowset size */
save_rowset_size = stmt->save_rowset_size;
stmt->save_rowset_size = -1;
switch (fFetchType) { switch (fFetchType) {
case SQL_FETCH_NEXT: case SQL_FETCH_NEXT:
/* From the odbc spec... If positioned before the start of the RESULT SET,
then this should be equivalent to SQL_FETCH_FIRST.
*/
if (stmt->rowset_start < 0)
stmt->rowset_start = 0;
else {
stmt->rowset_start += (save_rowset_size > 0 ? save_rowset_size : stmt->options.rowset_size);
}
mylog("SQL_FETCH_NEXT: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple); mylog("SQL_FETCH_NEXT: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
break; break;
case SQL_FETCH_PRIOR: case SQL_FETCH_PRIOR:
mylog("SQL_FETCH_PRIOR: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple); mylog("SQL_FETCH_PRIOR: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
/* If already before result set, return no data found */
if (stmt->currTuple <= 0)
return SQL_NO_DATA_FOUND;
stmt->currTuple -= 2; /* From the odbc spec... If positioned after the end of the RESULT SET,
then this should be equivalent to SQL_FETCH_LAST.
*/
if (stmt->rowset_start >= num_tuples) {
stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size);
}
else {
stmt->rowset_start -= stmt->options.rowset_size;
}
break; break;
case SQL_FETCH_FIRST: case SQL_FETCH_FIRST:
mylog("SQL_FETCH_FIRST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple); mylog("SQL_FETCH_FIRST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
stmt->currTuple = -1; stmt->rowset_start = 0;
break; break;
case SQL_FETCH_LAST: case SQL_FETCH_LAST:
mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple); mylog("SQL_FETCH_LAST: num_tuples=%d, currtuple=%d\n", num_tuples, stmt->currTuple);
stmt->currTuple = num_tuples <= 0 ? -1 : (num_tuples - 2);
stmt->rowset_start = num_tuples <= 0 ? 0 : (num_tuples - stmt->options.rowset_size) ;
break; break;
case SQL_FETCH_ABSOLUTE: case SQL_FETCH_ABSOLUTE:
@ -969,17 +1063,31 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt);
/* Position before result set, but dont fetch anything */ /* Position before result set, but dont fetch anything */
if (irow == 0) { if (irow == 0) {
stmt->rowset_start = -1;
stmt->currTuple = -1; stmt->currTuple = -1;
return SQL_NO_DATA_FOUND; return SQL_NO_DATA_FOUND;
} }
/* Position before the desired row */ /* Position before the desired row */
else if (irow > 0) { else if (irow > 0) {
stmt->currTuple = irow-2; stmt->rowset_start = irow - 1;
} }
/* Position with respect to the end of the result set */ /* Position with respect to the end of the result set */
else { else {
stmt->currTuple = num_tuples + irow - 1; stmt->rowset_start = num_tuples + irow;
} }
break;
case SQL_FETCH_RELATIVE:
/* Refresh the current rowset -- not currently implemented, but lie anyway */
if (irow == 0) {
break;
}
stmt->rowset_start += irow;
break; break;
default: default:
@ -988,18 +1096,95 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt);
} }
mylog("SQLExtendedFetch: new currTuple = %d\n", stmt->currTuple);
result = SQLFetch(hstmt); /***********************************/
/* CHECK FOR PROPER CURSOR STATE */
if (result == SQL_SUCCESS) { /***********************************/
if (rgfRowStatus) /* Handle Declare Fetch style specially because the end is not really the end... */
*rgfRowStatus = SQL_ROW_SUCCESS; if ( globals.use_declarefetch && ! stmt->manual_result) {
if (pcrow) if (QR_end_tuples(res)) {
*pcrow = 1; return SQL_NO_DATA_FOUND;
}
}
else {
/* If *new* rowset is after the result_set, return no data found */
if (stmt->rowset_start >= num_tuples) {
stmt->rowset_start = num_tuples;
return SQL_NO_DATA_FOUND;
}
} }
return result; /* If *new* rowset is prior to result_set, return no data found */
if (stmt->rowset_start < 0) {
if (stmt->rowset_start + stmt->options.rowset_size <= 0) {
stmt->rowset_start = -1;
return SQL_NO_DATA_FOUND;
}
else { /* overlap with beginning of result set, so get first rowset */
stmt->rowset_start = 0;
}
}
/* currTuple is always 1 row prior to the rowset */
stmt->currTuple = stmt->rowset_start - 1;
/* increment the base row in the tuple cache */
QR_set_rowset_size(res, stmt->options.rowset_size);
QR_inc_base(res, stmt->last_fetch_count);
/* Physical Row advancement occurs for each row fetched below */
mylog("SQLExtendedFetch: new currTuple = %d\n", stmt->currTuple);
truncated = error = FALSE;
for (i = 0; i < stmt->options.rowset_size; i++) {
stmt->bind_row = i; // set the binding location
result = SC_fetch(stmt);
/* Determine Function status */
if (result == SQL_NO_DATA_FOUND)
break;
else if (result == SQL_SUCCESS_WITH_INFO)
truncated = TRUE;
else if (result == SQL_ERROR)
error = TRUE;
/* Determine Row Status */
if (rgfRowStatus) {
if (result == SQL_ERROR)
*(rgfRowStatus + i) = SQL_ROW_ERROR;
else
*(rgfRowStatus + i)= SQL_ROW_SUCCESS;
}
}
/* Save the fetch count for SQLSetPos */
stmt->last_fetch_count= i;
/* Reset next binding row */
stmt->bind_row = 0;
/* Move the cursor position to the first row in the result set. */
stmt->currTuple = stmt->rowset_start;
/* For declare/fetch, need to reset cursor to beginning of rowset */
if (globals.use_declarefetch && ! stmt->manual_result) {
QR_set_position(res, 0);
}
/* Set the number of rows retrieved */
if (pcrow)
*pcrow = i;
if (i == 0)
return SQL_NO_DATA_FOUND; /* Only DeclareFetch should wind up here */
else if (error)
return SQL_ERROR;
else if (truncated)
return SQL_SUCCESS_WITH_INFO;
else
return SQL_SUCCESS;
} }
@ -1014,8 +1199,8 @@ RETCODE SQL_API SQLMoreResults(
return SQL_NO_DATA_FOUND; return SQL_NO_DATA_FOUND;
} }
// This positions the cursor within a block of data. // This positions the cursor within a rowset, that was positioned using SQLExtendedFetch.
// This will be useful (so far) only when using SQLGetData after SQLExtendedFetch.
RETCODE SQL_API SQLSetPos( RETCODE SQL_API SQLSetPos(
HSTMT hstmt, HSTMT hstmt,
UWORD irow, UWORD irow,
@ -1023,12 +1208,57 @@ RETCODE SQL_API SQLSetPos(
UWORD fLock) UWORD fLock)
{ {
static char *func = "SQLSetPos"; static char *func = "SQLSetPos";
char buf[128]; StatementClass *stmt = (StatementClass *) hstmt;
QResultClass *res;
int num_cols, i;
BindInfoClass *bindings = stmt->bindings;
sprintf(buf, "SQLSetPos not implemented: irow=%d, fOption=%d, fLock=%d\n", irow, fOption, fLock); if ( ! stmt) {
SC_log_error(func, "", NULL);
return SQL_INVALID_HANDLE;
}
if (fOption != SQL_POSITION && fOption != SQL_REFRESH) {
stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
stmt->errormsg = "Only SQL_POSITION/REFRESH is supported for SQLSetPos";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if ( ! (res = stmt->result)) {
stmt->errormsg = "Null statement result in SQLSetPos.";
stmt->errornumber = STMT_SEQUENCE_ERROR;
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
num_cols = QR_NumResultCols(res);
if (irow == 0) {
stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
stmt->errormsg = "Driver does not support Bulk operations.";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
if (irow > stmt->last_fetch_count) {
stmt->errornumber = STMT_ROW_OUT_OF_RANGE;
stmt->errormsg = "Row value out of range";
SC_log_error(func, "", stmt);
return SQL_ERROR;
}
irow--;
/* Reset for SQLGetData */
for (i = 0; i < num_cols; i++)
bindings[i].data_left = -1;
QR_set_position(res, irow);
stmt->currTuple = stmt->rowset_start + irow;
return SQL_SUCCESS;
SC_log_error(func, buf, (StatementClass *) hstmt);
return SQL_ERROR;
} }
// Sets options that control the behavior of cursors. // Sets options that control the behavior of cursors.

View File

@ -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

View File

@ -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;
/********************************************************************** /**********************************************************************
* 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;
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);
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);
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)
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;
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;
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;
/* 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) {
/* That worked, so now send the fetch to start getting data back */
sprintf(fetch, "fetch %d in %s", globals.fetch_max, self->cursor_name);
// Save the cursor in the result for later use QR_Destructor(self->result);
self->result = CC_send_query( conn, fetch, NULL, self->cursor_name);
/* That worked, so now send the fetch to start getting data back */
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);
self->result = CC_send_query( conn, fetch, &qi);
} }
mylog(" done sending the query:\n"); mylog(" done sending the query:\n");
@ -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;
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)
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");

View File

@ -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 {
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 {
} 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_ {
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_ {
/* 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);