From: Byron Nikolaidis Date: Fri, 8 Jan 1999 18:24:45 +0000 (+0000) Subject: Update 06-40-0004 -- Add Bookmark support! X-Git-Tag: REL6_5~825 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0b644ad332f5c9cfb074c41462e6274b72c39d5d;p=postgresql Update 06-40-0004 -- Add Bookmark support! --- diff --git a/src/interfaces/odbc/bind.c b/src/interfaces/odbc/bind.c index 512f352181..285fabd75a 100644 --- a/src/interfaces/odbc/bind.c +++ b/src/interfaces/odbc/bind.c @@ -162,13 +162,6 @@ mylog("**** SQLBindCol: stmt = %u, icol = %d\n", stmt, icol); 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); @@ -179,6 +172,28 @@ mylog("**** SQLBindCol: stmt = %u, icol = %d\n", stmt, icol); 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 diff --git a/src/interfaces/odbc/environ.c b/src/interfaces/odbc/environ.c index 1fa005b4c4..3190adfd8b 100644 --- a/src/interfaces/odbc/environ.c +++ b/src/interfaces/odbc/environ.c @@ -190,6 +190,11 @@ int status; 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 diff --git a/src/interfaces/odbc/info.c b/src/interfaces/odbc/info.c index 9a001264e6..9e019dbba3 100644 --- a/src/interfaces/odbc/info.c +++ b/src/interfaces/odbc/info.c @@ -107,8 +107,9 @@ RETCODE result; 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 */ @@ -221,7 +222,8 @@ RETCODE result; 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 */ diff --git a/src/interfaces/odbc/options.c b/src/interfaces/odbc/options.c index 6621acadf5..f16ec4bc4a 100644 --- a/src/interfaces/odbc/options.c +++ b/src/interfaces/odbc/options.c @@ -32,6 +32,7 @@ #include "environ.h" #include "connection.h" #include "statement.h" +#include "qresult.h" extern GLOBAL_VALUES globals; @@ -189,7 +190,6 @@ char changed = FALSE; if (conn) conn->stmtOptions.rowset_size = vParam; if (stmt) stmt->options.rowset_size = vParam; - break; case SQL_SIMULATE_CURSOR: /* NOT SUPPORTED */ @@ -205,18 +205,11 @@ char changed = FALSE; } 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: { @@ -507,6 +500,7 @@ RETCODE SQL_API SQLGetStmtOption( { static char *func="SQLGetStmtOption"; StatementClass *stmt = (StatementClass *) hstmt; +QResultClass *res; mylog("%s: entering...\n", func); @@ -520,15 +514,39 @@ StatementClass *stmt = (StatementClass *) hstmt; } 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 */ @@ -583,8 +601,8 @@ StatementClass *stmt = (StatementClass *) hstmt; *((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: diff --git a/src/interfaces/odbc/psqlodbc.h b/src/interfaces/odbc/psqlodbc.h index 8fcae8020b..8b095e335d 100644 --- a/src/interfaces/odbc/psqlodbc.h +++ b/src/interfaces/odbc/psqlodbc.h @@ -39,8 +39,8 @@ typedef UInt4 Oid; #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" @@ -137,6 +137,7 @@ typedef struct StatementOptions_ { 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 */ diff --git a/src/interfaces/odbc/psqlodbc.rc b/src/interfaces/odbc/psqlodbc.rc index fc22f3f371..bec4d3b889 100644 --- a/src/interfaces/odbc/psqlodbc.rc +++ b/src/interfaces/odbc/psqlodbc.rc @@ -204,8 +204,8 @@ END // 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 @@ -223,12 +223,12 @@ BEGIN VALUE "Comments", "PostgreSQL ODBC driver for Windows 95\0" VALUE "CompanyName", "Insight Distribution Systems\0" VALUE "FileDescription", "PostgreSQL Driver\0" - VALUE "FileVersion", " 6.40.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" diff --git a/src/interfaces/odbc/results.c b/src/interfaces/odbc/results.c index 7b2a0dc049..ece49dd8b0 100644 --- a/src/interfaces/odbc/results.c +++ b/src/interfaces/odbc/results.c @@ -610,7 +610,7 @@ int num_cols, num_rows; Int4 field_type; void *value; int result; - +char get_bookmark = FALSE; mylog("SQLGetData: enter, stmt=%u\n", stmt); @@ -635,24 +635,41 @@ 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); @@ -664,13 +681,16 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt); 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) { @@ -680,11 +700,21 @@ mylog("SQLGetData: enter, stmt=%u\n", stmt); 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); @@ -761,6 +791,14 @@ mylog("SQLFetch: stmt = %u, stmt->result= %u\n", stmt, stmt->result); 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; @@ -831,6 +869,14 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt); 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; @@ -950,6 +996,11 @@ mylog("SQLExtendedFetch: stmt=%u\n", stmt); break; + case SQL_FETCH_BOOKMARK: + + stmt->rowset_start = irow - 1; + break; + default: SC_log_error(func, "Unsupported SQLExtendedFetch Direction", stmt); return SQL_ERROR; diff --git a/src/interfaces/odbc/statement.c b/src/interfaces/odbc/statement.c index 86bce41a8f..e8a8b17b36 100644 --- a/src/interfaces/odbc/statement.c +++ b/src/interfaces/odbc/statement.c @@ -185,6 +185,7 @@ InitializeStatementOptions(StatementOptions *opt) 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 * @@ -213,6 +214,9 @@ StatementClass *rv; rv->bindings = NULL; rv->bindings_allocated = 0; + rv->bookmark.buffer = NULL; + rv->bookmark.used = NULL; + rv->parameters_allocated = 0; rv->parameters = 0; @@ -496,6 +500,9 @@ Int2 lf; self->bindings[lf].returntype = SQL_C_CHAR; } + self->bookmark.buffer = NULL; + self->bookmark.used = NULL; + return 1; } @@ -566,6 +573,15 @@ char rv; 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) @@ -624,6 +640,19 @@ ColumnInfoClass *ci; 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); diff --git a/src/interfaces/odbc/statement.h b/src/interfaces/odbc/statement.h index 82d047e1a1..d2d86e430a 100644 --- a/src/interfaces/odbc/statement.h +++ b/src/interfaces/odbc/statement.h @@ -15,6 +15,7 @@ #endif #include "psqlodbc.h" +#include "bind.h" #ifndef WIN32 #include "iodbc.h" @@ -71,6 +72,8 @@ typedef enum { #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 { @@ -142,6 +145,7 @@ struct StatementClass_ { /* information on bindings */ BindInfoClass *bindings; /* array to store the binding information */ + BindInfoClass bookmark; int bindings_allocated; /* information on statement parameters */ @@ -207,6 +211,7 @@ RETCODE SC_execute(StatementClass *self); 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