return SQL_INVALID_HANDLE;
}
- if (icol < 1) {
- /* currently we do not support bookmarks */
- stmt->errormsg = "Bookmarks are not currently supported.";
- stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
- SC_log_error(func, "", stmt);
- return SQL_ERROR;
- }
SC_clear_error(stmt);
return SQL_ERROR;
}
+ /* If the bookmark column is being bound, then just save it */
+ if (icol == 0) {
+
+ if (rgbValue == NULL) {
+ stmt->bookmark.buffer = NULL;
+ stmt->bookmark.used = NULL;
+ }
+ else {
+ /* Make sure it is the bookmark data type */
+ if ( fCType != SQL_C_BOOKMARK) {
+ stmt->errormsg = "Column 0 is not of type SQL_C_BOOKMARK";
+ stmt->errornumber = STMT_PROGRAM_TYPE_OUT_OF_RANGE;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ stmt->bookmark.buffer = rgbValue;
+ stmt->bookmark.used = pcbValue;
+ }
+ return SQL_SUCCESS;
+ }
+
// allocate enough bindings if not already done
// Most likely, execution of a statement would have setup the
// necessary bindings. But some apps call BindCol before any
case STMT_VALUE_OUT_OF_RANGE:
strcpy(szSqlState, "22003");
break;
+
+ case STMT_OPERATION_INVALID:
+ strcpy(szSqlState, "S1011");
+ break;
+
default:
strcpy(szSqlState, "S1000");
// also a general error
break;
case SQL_BOOKMARK_PERSISTENCE: /* ODBC 2.0 */
- len = 4;
- value = 0;
+ /* very simple bookmark support */
+ len = 4;
+ value = globals.use_declarefetch ? 0 : (SQL_BP_SCROLL);
break;
case SQL_COLUMN_ALIAS: /* ODBC 2.0 */
SQL_FD_FETCH_LAST |
SQL_FD_FETCH_PRIOR |
SQL_FD_FETCH_ABSOLUTE |
- SQL_FD_FETCH_RELATIVE);
+ SQL_FD_FETCH_RELATIVE |
+ SQL_FD_FETCH_BOOKMARK);
break;
case SQL_FILE_USAGE: /* ODBC 2.0 */
#include "environ.h"
#include "connection.h"
#include "statement.h"
+#include "qresult.h"
extern GLOBAL_VALUES globals;
if (conn) conn->stmtOptions.rowset_size = vParam;
if (stmt) stmt->options.rowset_size = vParam;
-
break;
case SQL_SIMULATE_CURSOR: /* NOT SUPPORTED */
}
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;
+ case SQL_USE_BOOKMARKS:
+
+ if (stmt) stmt->options.use_bookmarks = vParam;
+ if (conn) conn->stmtOptions.use_bookmarks = vParam;
+ break;
default:
{
{
static char *func="SQLGetStmtOption";
StatementClass *stmt = (StatementClass *) hstmt;
+QResultClass *res;
mylog("%s: entering...\n", func);
}
switch(fOption) {
- case SQL_GET_BOOKMARK:/* NOT SUPPORTED */
- stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
- stmt->errormsg = "Driver does not support getting bookmarks.";
- SC_log_error(func, "", stmt);
- return SQL_ERROR;
- break;
-
+ case SQL_GET_BOOKMARK:
case SQL_ROW_NUMBER:
- *((SDWORD *) pvParam) = stmt->currTuple + 1;
+
+ res = stmt->result;
+
+ if ( stmt->manual_result || ! globals.use_declarefetch) {
+ // make sure we're positioned on a valid row
+ if((stmt->currTuple < 0) ||
+ (stmt->currTuple >= QR_get_num_tuples(res))) {
+ stmt->errormsg = "Not positioned on a valid row.";
+ stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ }
+ else {
+ if (stmt->currTuple == -1 || ! res || ! res->tupleField) {
+ stmt->errormsg = "Not positioned on a valid row.";
+ stmt->errornumber = STMT_INVALID_CURSOR_STATE_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ }
+
+ if (fOption == SQL_GET_BOOKMARK && stmt->options.use_bookmarks == SQL_UB_OFF) {
+ stmt->errormsg = "Operation invalid because use bookmarks not enabled.";
+ stmt->errornumber = STMT_OPERATION_INVALID;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ *((UDWORD *) pvParam) = SC_get_bookmark(stmt);
+
break;
case SQL_ASYNC_ENABLE: /* NOT SUPPORTED */
*((SDWORD *) pvParam) = SQL_SC_NON_UNIQUE;
break;
- case SQL_USE_BOOKMARKS:/* NOT SUPPORTED */
- *((SDWORD *) pvParam) = SQL_UB_OFF;
+ case SQL_USE_BOOKMARKS:
+ *((SDWORD *) pvParam) = stmt->options.use_bookmarks;
break;
default:
#define DRIVERNAME "PostgreSQL ODBC"
#define DBMS_NAME "PostgreSQL"
-#define DBMS_VERSION "06.40.0003 PostgreSQL 6.4"
-#define POSTGRESDRIVERVERSION "06.40.0003"
+#define DBMS_VERSION "06.40.0004 PostgreSQL 6.4"
+#define POSTGRESDRIVERVERSION "06.40.0004"
#ifdef WIN32
#define DRIVER_FILE_NAME "PSQLODBC.DLL"
int scroll_concurrency;
int retrieve_data;
int bind_size; /* size of each structure if using Row Binding */
+ int use_bookmarks;
} StatementOptions;
/* Used to pass extra query info to send_query */
//
VS_VERSION_INFO VERSIONINFO
- FILEVERSION 6,40,0,3
- PRODUCTVERSION 6,40,0,3
+ FILEVERSION 6,40,0,4
+ PRODUCTVERSION 6,40,0,4
FILEFLAGSMASK 0x3L
#ifdef _DEBUG
FILEFLAGS 0x1L
VALUE "Comments", "PostgreSQL ODBC driver for Windows 95\0"
VALUE "CompanyName", "Insight Distribution Systems\0"
VALUE "FileDescription", "PostgreSQL Driver\0"
- VALUE "FileVersion", " 6.40.0003\0"
+ VALUE "FileVersion", " 6.40.0004\0"
VALUE "InternalName", "psqlodbc\0"
VALUE "LegalTrademarks", "ODBC(TM) is a trademark of Microsoft Corporation. Microsoft® is a registered trademark of Microsoft Corporation. Windows(TM) is a trademark of Microsoft Corporation.\0"
VALUE "OriginalFilename", "psqlodbc.dll\0"
VALUE "ProductName", "Microsoft Open Database Connectivity\0"
- VALUE "ProductVersion", " 6.40.0003\0"
+ VALUE "ProductVersion", " 6.40.0004\0"
END
END
BLOCK "VarFileInfo"
Int4 field_type;
void *value;
int result;
-
+char get_bookmark = FALSE;
mylog("SQLGetData: enter, stmt=%u\n", stmt);
}
if (icol == 0) {
- stmt->errormsg = "Bookmarks are not currently supported.";
- stmt->errornumber = STMT_NOT_IMPLEMENTED_ERROR;
- SC_log_error(func, "", stmt);
- return SQL_ERROR;
- }
- // use zero-based column numbers
- icol--;
+ if (stmt->options.use_bookmarks == SQL_UB_OFF) {
+ stmt->errornumber = STMT_COLNUM_ERROR;
+ stmt->errormsg = "Attempt to retrieve bookmark with bookmark usage disabled";
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ /* Make sure it is the bookmark data type */
+ if (fCType != SQL_C_BOOKMARK) {
+ stmt->errormsg = "Column 0 is not of type SQL_C_BOOKMARK";
+ stmt->errornumber = STMT_PROGRAM_TYPE_OUT_OF_RANGE;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+
+ get_bookmark = TRUE;
- // make sure the column number is valid
- num_cols = QR_NumResultCols(res);
- if (icol >= num_cols) {
- stmt->errormsg = "Invalid column number.";
- stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
- SC_log_error(func, "", stmt);
- return SQL_ERROR;
}
+ else {
+
+ // use zero-based column numbers
+ icol--;
+
+ // make sure the column number is valid
+ num_cols = QR_NumResultCols(res);
+ if (icol >= num_cols) {
+ stmt->errormsg = "Invalid column number.";
+ stmt->errornumber = STMT_INVALID_COLUMN_NUMBER_ERROR;
+ SC_log_error(func, "", stmt);
+ return SQL_ERROR;
+ }
+ }
+
if ( stmt->manual_result || ! globals.use_declarefetch) {
// make sure we're positioned on a valid row
num_rows = QR_get_num_tuples(res);
return SQL_ERROR;
}
mylog(" num_rows = %d\n", num_rows);
- if ( stmt->manual_result) {
- value = QR_get_value_manual(res, stmt->currTuple, icol);
- }
- else {
- value = QR_get_value_backend_row(res, stmt->currTuple, icol);
+
+ if ( ! get_bookmark) {
+ if ( stmt->manual_result) {
+ value = QR_get_value_manual(res, stmt->currTuple, icol);
+ }
+ else {
+ value = QR_get_value_backend_row(res, stmt->currTuple, icol);
+ }
+ mylog(" value = '%s'\n", value);
}
- mylog(" value = '%s'\n", value);
}
else { /* its a SOCKET result (backend data) */
if (stmt->currTuple == -1 || ! res || ! res->tupleField) {
return SQL_ERROR;
}
- value = QR_get_value_backend(res, icol);
+ if ( ! get_bookmark)
+ value = QR_get_value_backend(res, icol);
mylog(" socket: value = '%s'\n", value);
}
+ if ( get_bookmark) {
+ *((UDWORD *) rgbValue) = SC_get_bookmark(stmt);
+
+ if (pcbValue)
+ *pcbValue = 4;
+
+ return SQL_SUCCESS;
+ }
+
field_type = QR_get_field_type(res, icol);
mylog("**** SQLGetData: icol = %d, fCType = %d, field_type = %d, value = '%s'\n", icol, fCType, field_type, value);
return SQL_ERROR;
}
+ /* Not allowed to bind a bookmark column when using SQLFetch. */
+ if ( stmt->bookmark.buffer) {
+ stmt->errornumber = STMT_COLNUM_ERROR;
+ stmt->errormsg = "Not allowed to bind a bookmark column when using SQLFetch";
+ 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;
return SQL_ERROR;
}
+ /* If a bookmark colunmn is bound but bookmark usage is off, then error */
+ if (stmt->bookmark.buffer && stmt->options.use_bookmarks == SQL_UB_OFF) {
+ stmt->errornumber = STMT_COLNUM_ERROR;
+ stmt->errormsg = "Attempt to retrieve bookmark with bookmark usage disabled";
+ 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;
break;
+ case SQL_FETCH_BOOKMARK:
+
+ stmt->rowset_start = irow - 1;
+ break;
+
default:
SC_log_error(func, "Unsupported SQLExtendedFetch Direction", stmt);
return SQL_ERROR;
opt->cursor_type = SQL_CURSOR_FORWARD_ONLY;
opt->bind_size = 0; /* default is to bind by column */
opt->retrieve_data = SQL_RD_ON;
+ opt->use_bookmarks = SQL_UB_OFF;
}
StatementClass *
rv->bindings = NULL;
rv->bindings_allocated = 0;
+ rv->bookmark.buffer = NULL;
+ rv->bookmark.used = NULL;
+
rv->parameters_allocated = 0;
rv->parameters = 0;
self->bindings[lf].returntype = SQL_C_CHAR;
}
+ self->bookmark.buffer = NULL;
+ self->bookmark.used = NULL;
+
return 1;
}
return rv;
}
+/* Currently, the driver offers very simple bookmark support -- it is
+ just the current row number. But it could be more sophisticated
+ someday, such as mapping a key to a 32 bit value
+*/
+unsigned long
+SC_get_bookmark(StatementClass *self)
+{
+ return (self->currTuple + 1);
+}
RETCODE
SC_fetch(StatementClass *self)
result = SQL_SUCCESS;
self->last_fetch_count = 1;
+ /* If the bookmark column was bound then return a bookmark.
+ Since this is used with SQLExtendedFetch, and the rowset size
+ may be greater than 1, and an application can use row or column wise
+ binding, use the code in copy_and_convert_field() to handle that.
+ */
+ if (self->bookmark.buffer) {
+ char buf[32];
+
+ sprintf(buf, "%ld", SC_get_bookmark(self));
+ result = copy_and_convert_field(self, 0, buf,
+ SQL_C_ULONG, self->bookmark.buffer, 0, self->bookmark.used);
+ }
+
for (lf=0; lf < num_cols; lf++) {
mylog("fetch: cols=%d, lf=%d, self = %u, self->bindings = %u, buffer[] = %u\n", num_cols, lf, self, self->bindings, self->bindings[lf].buffer);
#endif
#include "psqlodbc.h"
+#include "bind.h"
#ifndef WIN32
#include "iodbc.h"
#define STMT_OPERATION_CANCELLED 22
#define STMT_INVALID_CURSOR_POSITION 23
#define STMT_VALUE_OUT_OF_RANGE 24
+#define STMT_OPERATION_INVALID 25
+#define STMT_PROGRAM_TYPE_OUT_OF_RANGE 26
/* statement types */
enum {
/* information on bindings */
BindInfoClass *bindings; /* array to store the binding information */
+ BindInfoClass bookmark;
int bindings_allocated;
/* information on statement parameters */
RETCODE SC_fetch(StatementClass *self);
void SC_free_params(StatementClass *self, char option);
void SC_log_error(char *func, char *desc, StatementClass *self);
+unsigned long SC_get_bookmark(StatementClass *self);
#endif