Re: Problem retrieveing results from bottom to top
Frediano Ziglio <[email protected]>
| Newsgroups | gmane.comp.db.tds.freetds |
|---|---|
| Message-ID | <1213779723.7316.10.camel@freddy> |
Il giorno mar, 17/06/2008 alle 18.28 -0400, James K. Lowden ha scritto: > Daniel A. Veiga wrote: > > I believe the approach of MSSQL ODBC driver (fetching the row > > number only when needed) is better than the one I found in the > > jDBC driver. So I implemented > > SQLGetStmtAttr(SQL_ATTR_ROW_NUMBER) that way. > > Hi Daniel, > > I don't want your patch to get lost; it's nice work. When you don't see > an answer to serious work on this list for a few days, it's usually > because it need serious consideration and no one's been able to look at it > carefully. > > Your patch is a little difficult to apply easily because of how it's > generated. Could I ask you to redo it? > > The command you want is: > > $ diff -u /path/to/freetds-orig . > odbc.cursor7.diff > > while '.' represents the top of your patched FreeTDS source tree. > > A universal diff is much easier to review (for these eyes, at least) and > easier to edit and apply. If you don't do that, I'll have to apply each > patch one at at time, then use CVS to generate a universal (-u) diff, and > start again. > yes, it would help, mainly for tds.h patch, it's not that easy to understand where to put the added line... > Unless Frediano objects, I expect to review and apply your patch more or > less as is and let the chips fall where they may. If it creates a > problem, we can always back it out. Of course, I'm not expecting > problems. > I would change some small points but mainly the patch is fine: - INIT_HTSMT is not necessary at that point - tds_cursor_get_cursor_info should return TDS_SUCCEED/TDS_FAIL attached an update patch. result Creating table #cursor7_test with 12 records. CREATE TABLE #cursor7_test (i INT, c VARCHAR(20)) INSERT INTO #cursor7_test(i,c) VALUES(1, 'a1b1c1') INSERT INTO #cursor7_test(i,c) VALUES(2, 'a2b2c2') INSERT INTO #cursor7_test(i,c) VALUES(3, 'a3b3c3') INSERT INTO #cursor7_test(i,c) VALUES(4, 'a4b4c4') INSERT INTO #cursor7_test(i,c) VALUES(5, 'a5b5c5') INSERT INTO #cursor7_test(i,c) VALUES(6, 'a6b6c6') INSERT INTO #cursor7_test(i,c) VALUES(7, 'a7b7c7') INSERT INTO #cursor7_test(i,c) VALUES(8, 'a8b8c8') INSERT INTO #cursor7_test(i,c) VALUES(9, 'a9b9c9') INSERT INTO #cursor7_test(i,c) VALUES(10, 'a10b10c10') INSERT INTO #cursor7_test(i,c) VALUES(11, 'a11b11c11') INSERT INTO #cursor7_test(i,c) VALUES(12, 'a12b12c12') Reading records from last to first: 12, a12b12c12 11, a11b11c11 10, a10b10c10 9, a9b9c9 8, a8b8c8 ---> We are in record No: 8 7, a7b7c7 6, a6b6c6 5, a5b5c5 4, a4b4c4 3, a3b3c3 ---> We are in record No: 3 2, a2b2c2 1, a1b1c1 ---> We are in record No: 1 which is correct! Perhaps we should try to convert to int with tds_convert insted of doing all data check in tds_cursor_get_cursor_info. freddy77 _______________________________________________ FreeTDS mailing list [email protected] http://lists.ibiblio.org/mailman/listinfo/freetds
cursor7.diff
(text/x-patch, 10.1 KB)
Index: include/tds.h
===================================================================
RCS file: /cvsroot/freetds/freetds/include/tds.h,v
retrieving revision 1.288
diff -U10 -r1.288 tds.h
--- include/tds.h 27 May 2008 22:24:49 -0000 1.288
+++ include/tds.h 18 Jun 2008 08:51:26 -0000
@@ -1477,20 +1477,21 @@
int tds_submit_rpc(TDSSOCKET * tds, const char *rpc_name, TDSPARAMINFO * params);
int tds_submit_optioncmd(TDSSOCKET * tds, TDS_OPTION_CMD command, TDS_OPTION option, TDS_OPTION_ARG *param, TDS_INT param_size);
int tds_quote_id(TDSSOCKET * tds, char *buffer, const char *id, int idlen);
int tds_quote_string(TDSSOCKET * tds, char *buffer, const char *str, int len);
const char *tds_skip_quoted(const char *s);
int tds_cursor_declare(TDSSOCKET * tds, TDSCURSOR * cursor, TDSPARAMINFO *params, int *send);
int tds_cursor_setrows(TDSSOCKET * tds, TDSCURSOR * cursor, int *send);
int tds_cursor_open(TDSSOCKET * tds, TDSCURSOR * cursor, TDSPARAMINFO *params, int *send);
int tds_cursor_fetch(TDSSOCKET * tds, TDSCURSOR * cursor, TDS_CURSOR_FETCH fetch_type, TDS_INT i_row);
+int tds_cursor_get_cursor_info(TDSSOCKET * tds, TDSCURSOR * cursor, TDS_UINT * row_number, TDS_UINT * row_count);
int tds_cursor_close(TDSSOCKET * tds, TDSCURSOR * cursor);
int tds_cursor_dealloc(TDSSOCKET * tds, TDSCURSOR * cursor);
int tds_cursor_update(TDSSOCKET * tds, TDSCURSOR * cursor, TDS_CURSOR_OPERATION op, TDS_INT i_row, TDSPARAMINFO * params);
int tds_cursor_setname(TDSSOCKET * tds, TDSCURSOR * cursor);
int tds_multiple_init(TDSSOCKET *tds, TDSMULTIPLE *multiple, TDS_MULTIPLE_TYPE type);
int tds_multiple_done(TDSSOCKET *tds, TDSMULTIPLE *multiple);
int tds_multiple_query(TDSSOCKET *tds, TDSMULTIPLE *multiple, const char *query, TDSPARAMINFO * params);
int tds_multiple_execute(TDSSOCKET *tds, TDSMULTIPLE *multiple, TDSDYNAMIC * dyn);
Index: src/odbc/odbc.c
===================================================================
RCS file: /cvsroot/freetds/freetds/src/odbc/odbc.c,v
retrieving revision 1.482
diff -U10 -r1.482 odbc.c
--- src/odbc/odbc.c 12 Jun 2008 03:10:16 -0000 1.482
+++ src/odbc/odbc.c 18 Jun 2008 08:51:28 -0000
@@ -4087,21 +4129,27 @@
src = &stmt->ard->header.sql_desc_bind_offset_ptr;
break;
#if SQL_BIND_TYPE != SQL_ATTR_ROW_BIND_TYPE
case SQL_BIND_TYPE: /* although this is ODBC2 we must support this attribute */
#endif
case SQL_ATTR_ROW_BIND_TYPE:
size = sizeof(stmt->ard->header.sql_desc_bind_type);
src = &stmt->ard->header.sql_desc_bind_type;
break;
case SQL_ATTR_ROW_NUMBER:
- /* TODO update this value */
+ /* TODO do not get info every time, cache somewhere */
+ if (stmt->cursor && odbc_lock_statement(stmt)) {
+ TDS_UINT row_number, row_count;
+
+ tds_cursor_get_cursor_info(stmt->dbc->tds_socket, stmt->cursor, &row_number, &row_count);
+ stmt->attr.row_number = row_number;
+ }
size = sizeof(stmt->attr.row_number);
src = &stmt->attr.row_number;
break;
case SQL_ATTR_ROW_OPERATION_PTR:
size = sizeof(stmt->ard->header.sql_desc_array_status_ptr);
src = &stmt->ard->header.sql_desc_array_status_ptr;
break;
case SQL_ATTR_ROW_STATUS_PTR:
size = sizeof(stmt->ird->header.sql_desc_array_status_ptr);
src = &stmt->ird->header.sql_desc_array_status_ptr;
Index: src/odbc/unittests/cursor7.c
===================================================================
RCS file: /cvsroot/freetds/freetds/src/odbc/unittests/cursor7.c,v
retrieving revision 1.1
diff -U10 -r1.1 cursor7.c
--- src/odbc/unittests/cursor7.c 12 Jun 2008 03:52:05 -0000 1.1
+++ src/odbc/unittests/cursor7.c 18 Jun 2008 08:51:29 -0000
@@ -1,29 +1,30 @@
#include "common.h"
-/* Test SQLFetchScroll with no bound columns */
+/* Test SQLFetchScroll with a non-unitary rowset, using bottom-up direction */
static char software_version[] = "$Id: cursor7.c,v 1.1 2008/06/12 03:52:05 jklowden Exp $";
static void *no_unused_var_warn[] = { software_version, no_unused_var_warn };
static void
Test(void)
{
enum { ROWS=5 };
struct data_t {
SQLINTEGER i;
SQLLEN ind_i;
char c[20];
SQLLEN ind_c;
} data[ROWS];
SQLUSMALLINT statuses[ROWS];
SQLULEN num_row;
+ SQLULEN RowNumber;
int i;
SQLRETURN ErrCode;
ResetStatement();
CHK(SQLSetStmtAttr, (Statement, SQL_ATTR_CONCURRENCY, int2ptr(SQL_CONCUR_READ_ONLY), 0));
CHK(SQLSetStmtAttr, (Statement, SQL_ATTR_CURSOR_TYPE, int2ptr(SQL_CURSOR_STATIC), 0));
CHK(SQLPrepare, (Statement, (SQLCHAR *) "SELECT c, i FROM #cursor7_test", SQL_NTS));
@@ -33,30 +34,40 @@
CHK(SQLSetStmtAttr, (Statement, SQL_ATTR_ROW_STATUS_PTR, statuses, 0));
CHK(SQLSetStmtAttr, (Statement, SQL_ATTR_ROWS_FETCHED_PTR, &num_row, 0));
CHK(SQLBindCol, (Statement, 1, SQL_C_CHAR, &data[0].c, sizeof(data[0].c), &data[0].ind_c));
CHK(SQLBindCol, (Statement, 2, SQL_C_LONG, &data[0].i, sizeof(data[0].i), &data[0].ind_i));
/* Read records from last to first */
printf("\n\nReading records from last to first:\n");
ErrCode = SQLFetchScroll(Statement, SQL_FETCH_LAST, -ROWS);
while ((ErrCode == SQL_SUCCESS) || (ErrCode == SQL_SUCCESS_WITH_INFO)) {
+ SQLULEN RowNumber;
+
/* Print this set of rows */
for (i = ROWS - 1; i >= 0; i--) {
if (statuses[i] != SQL_ROW_NOROW)
printf("\t %d, %s\n", (int) data[i].i, data[i].c);
}
+ CHK(SQLGetStmtAttr, (Statement, SQL_ROW_NUMBER, (SQLPOINTER)(&RowNumber), sizeof(RowNumber), NULL));
+ printf("---> We are in record No: %u\n", (unsigned int) RowNumber);
+
/* Read next rowset */
- ErrCode = SQLFetchScroll(Statement, SQL_FETCH_RELATIVE, -ROWS);
+ if ( (RowNumber>1) && (RowNumber<ROWS) ) {
+ ErrCode=SQLFetchScroll(Statement, SQL_FETCH_RELATIVE, 1-RowNumber);
+ for (i=RowNumber-1; i<ROWS; ++i)
+ statuses[i] = SQL_ROW_NOROW;
+ } else {
+ ErrCode=SQLFetchScroll(Statement, SQL_FETCH_RELATIVE, -ROWS);
+ }
}
- printf("\nRecords 5, 4 and 3 are returned twice!!\n\n");
}
static void
Init(void)
{
int i;
char sql[128];
printf("\n\nCreating table #cursor7_test with 12 records.\n");
Index: src/tds/query.c
===================================================================
RCS file: /cvsroot/freetds/freetds/src/tds/query.c,v
retrieving revision 1.219
diff -U10 -r1.219 query.c
--- src/tds/query.c 13 Feb 2008 08:52:09 -0000 1.219
+++ src/tds/query.c 18 Jun 2008 08:51:31 -0000
@@ -2412,20 +2413,133 @@
tds->internal_sp_called = TDS_SP_CURSORFETCH;
return tds_query_flush_packet(tds);
}
tds_set_state(tds, TDS_IDLE);
return TDS_SUCCEED;
}
int
+tds_cursor_get_cursor_info(TDSSOCKET * tds, TDSCURSOR * cursor, TDS_UINT * row_number, TDS_UINT * row_count)
+{
+ int done_flags;
+ int retcode;
+ TDS_INT result_type;
+
+ CHECK_TDS_EXTRA(tds);
+
+ if (!cursor)
+ return TDS_FAIL;
+
+ tdsdump_log(TDS_DBG_INFO1, "tds_cursor_get_cursor_info() cursor id = %d\n", cursor->cursor_id);
+
+ /* Assume not known */
+ *row_number = 0;
+ *row_count = 0;
+
+ if (IS_TDS7_PLUS(tds)) {
+ /* Change state to quering */
+ if (tds_set_state(tds, TDS_QUERYING) != TDS_QUERYING)
+ return TDS_FAIL;
+
+ /* Remember the server has been sent a command for this cursor */
+ tds_set_cur_cursor(tds, cursor);
+
+ /* General initialization of server command */
+ tds->out_flag = TDS_RPC;
+ START_QUERY;
+
+ /* Create and send query to server */
+ if (IS_TDS8_PLUS(tds)) {
+ tds_put_smallint(tds, -1);
+ tds_put_smallint(tds, TDS_SP_CURSORFETCH);
+ } else {
+ tds_put_smallint(tds, 14);
+ TDS_PUT_N_AS_UCS2(tds, "sp_cursorfetch");
+ }
+
+ /* This flag tells the SP only to */
+ /* output a dummy metadata token */
+
+ tds_put_smallint(tds, 2);
+
+ /* input cursor handle (int) */
+
+ tds_put_byte(tds, 0); /* no parameter name */
+ tds_put_byte(tds, 0); /* input parameter */
+ tds_put_byte(tds, SYBINTN);
+ tds_put_byte(tds, 4);
+ tds_put_byte(tds, 4);
+ tds_put_int(tds, cursor->cursor_id);
+
+ tds_put_byte(tds, 0); /* no parameter name */
+ tds_put_byte(tds, 0); /* input parameter */
+ tds_put_byte(tds, SYBINTN);
+ tds_put_byte(tds, 4);
+ tds_put_byte(tds, 4);
+ tds_put_int(tds, 0x100); /* FETCH_INFO */
+
+ /* row number */
+ tds_put_byte(tds, 0); /* no parameter name */
+ tds_put_byte(tds, 1); /* output parameter */
+ tds_put_byte(tds, SYBINTN);
+ tds_put_byte(tds, 4);
+ tds_put_byte(tds, 0);
+
+ /* number of rows fetched */
+ tds_put_byte(tds, 0); /* no parameter name */
+ tds_put_byte(tds, 1); /* output parameter */
+ tds_put_byte(tds, SYBINTN);
+ tds_put_byte(tds, 4);
+ tds_put_byte(tds, 0);
+
+ /* Adjust current state */
+ tds->internal_sp_called = 0;
+ if ( (retcode=tds_query_flush_packet(tds)) != TDS_SUCCEED )
+ return retcode;
+
+ /* Process answer from server */
+ for (;;) {
+ retcode = tds_process_tokens(tds, &result_type, &done_flags, TDS_RETURN_PROC);
+ tdsdump_log(TDS_DBG_FUNC, "tds_cursor_get_cursor_info: tds_process_tokens returned %d\n", retcode);
+ tdsdump_log(TDS_DBG_FUNC, " result_type=%d, TDS_DONE_COUNT=%x, TDS_DONE_ERROR=%x\n", result_type, (done_flags & TDS_DONE_COUNT), (done_flags & TDS_DONE_ERROR));
+ switch (retcode) {
+ case TDS_NO_MORE_RESULTS:
+ return TDS_SUCCEED;
+ case TDS_CANCELLED:
+ case TDS_FAIL:
+ return TDS_FAIL;
+
+ case TDS_SUCCEED:
+ if (result_type==TDS_PARAM_RESULT) {
+ /* Status is updated when TDS_STATUS_RESULT token arrives, before the params are processed */
+ if (tds->has_status && tds->ret_status==0) {
+ TDSPARAMINFO *pinfo = tds->current_results;
+
+ /* Make sure the params retuned have the correct tipe and size */
+ if (pinfo && pinfo->num_cols==2 && pinfo->columns[0]->column_type==SYBINTN && pinfo->columns[1]->column_type==SYBINTN && pinfo->columns[0]->column_size==4 && pinfo->columns[1]->column_size==4) {
+ /* Take the values */
+ *row_number = (TDS_UINT)(*(TDS_INT *) pinfo->columns[0]->column_data);
+ *row_count = (TDS_UINT)(*(TDS_INT *) pinfo->columns[1]->column_data);
+ tdsdump_log(TDS_DBG_FUNC, "----------------> row_number=%u, row_count=%u\n", row_count, row_number);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return TDS_SUCCEED;
+}
+
+int
tds_cursor_close(TDSSOCKET * tds, TDSCURSOR * cursor)
{
CHECK_TDS_EXTRA(tds);
if (!cursor)
return TDS_FAIL;
tdsdump_log(TDS_DBG_INFO1, "tds_cursor_close() cursor id = %d\n", cursor->cursor_id);
if (tds_set_state(tds, TDS_QUERYING) != TDS_QUERYING)