Update for version 6-40-0002 and re-merge Thomas' changes.
This commit is contained in:
parent
d73cf044f6
commit
6d7735e7f0
|
@ -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;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
*/
|
*/
|
||||||
struct BindInfoClass_ {
|
struct BindInfoClass_ {
|
||||||
Int4 buflen; /* size of buffer */
|
Int4 buflen; /* size of buffer */
|
||||||
|
Int4 data_left; /* amount of data left to read (SQLGetData) */
|
||||||
char *buffer; /* pointer to the buffer */
|
char *buffer; /* pointer to the buffer */
|
||||||
Int4 *used; /* used space in the buffer (for strings not counting the '\0') */
|
Int4 *used; /* used space in the buffer (for strings not counting the '\0') */
|
||||||
Int2 returntype; /* kind of conversion to be applied when returning (SQL_C_DEFAULT, SQL_C_CHAR...) */
|
Int2 returntype; /* kind of conversion to be applied when returning (SQL_C_DEFAULT, SQL_C_CHAR...) */
|
||||||
|
|
|
@ -31,7 +31,6 @@
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 */
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -44,13 +44,13 @@ generate_filename(char* dirname,char* prefix,char* filename)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
strcpy(filename,dirname);
|
strcpy(filename,dirname);
|
||||||
strcat(filename,DIRSEPERATOR);
|
strcat(filename,DIRSEPARATOR);
|
||||||
if(prefix != 0)
|
if(prefix != 0)
|
||||||
strcat(filename,prefix);
|
strcat(filename,prefix);
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
strcat(filename,ptr->pw_name);
|
strcat(filename,ptr->pw_name);
|
||||||
#endif
|
#endif
|
||||||
sprintf(filename,"%s%d%s",filename,pid,".log");
|
sprintf(filename,"%s%u%s",filename,pid,".log");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -39,33 +39,41 @@
|
||||||
|
|
||||||
|
|
||||||
#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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -61,10 +61,12 @@
|
||||||
#define PG_TYPE_TIMESTAMP 1296
|
#define PG_TYPE_TIMESTAMP 1296
|
||||||
|
|
||||||
extern Int4 pgtypes_defined[];
|
extern Int4 pgtypes_defined[];
|
||||||
|
extern Int2 sqlTypes[];
|
||||||
|
|
||||||
/* Defines for pgtype_precision */
|
/* Defines for pgtype_precision */
|
||||||
#define PG_STATIC -1
|
#define PG_STATIC -1
|
||||||
|
|
||||||
|
Int4 sqltype_to_pgtype(Int2 fSqlType);
|
||||||
|
|
||||||
Int2 pgtype_to_sqltype(StatementClass *stmt, Int4 type);
|
Int2 pgtype_to_sqltype(StatementClass *stmt, Int4 type);
|
||||||
Int2 pgtype_to_ctype(StatementClass *stmt, Int4 type);
|
Int2 pgtype_to_ctype(StatementClass *stmt, Int4 type);
|
||||||
|
|
|
@ -126,4 +126,3 @@ RETCODE SQL_API SQLDummyOrdinal(void)
|
||||||
return SQL_SUCCESS;
|
return SQL_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -21,8 +21,18 @@
|
||||||
#include <netdb.h>
|
#include <netdb.h>
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
#define closesocket(xxx) close(xxx)
|
#define closesocket(xxx) close(xxx)
|
||||||
#define SOCKETFD int
|
#define SOCKETFD int
|
||||||
|
|
||||||
|
#ifndef INADDR_NONE
|
||||||
|
#ifndef _IN_ADDR_T
|
||||||
|
#define _IN_ADDR_T
|
||||||
|
typedef unsigned int in_addr_t;
|
||||||
|
#endif
|
||||||
|
#define INADDR_NONE ((in_addr_t)-1)
|
||||||
|
#endif
|
||||||
|
|
||||||
#else
|
#else
|
||||||
#include <winsock.h>
|
#include <winsock.h>
|
||||||
#define SOCKETFD SOCKET
|
#define SOCKETFD SOCKET
|
||||||
|
|
|
@ -97,6 +97,14 @@ StatementClass *stmt;
|
||||||
|
|
||||||
*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");
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue