Skip to content
Permalink
Browse files

ODBC-196 Optimization for FORWARD_ONLY cursors

in case of rows array fetch. Some optimization are done for other
cursors as well - i.e. for static and dynamic. Especially if static
navigated forward.
Removed some workaround for the previous release build, and updated
C/C sub-project.
  • Loading branch information
lawrinn committed Sep 24, 2019
1 parent 579c9b8 commit f098cc97e0bc402598a7e5b5de6d799d1fce61ec
Showing with 104 additions and 101 deletions.
  1. +0 −2 CMakeLists.txt
  2. +1 −1 libmariadb
  3. +4 −3 ma_odbc.h
  4. +44 −0 ma_result.c
  5. +1 −0 ma_result.h
  6. +43 −80 ma_statement.c
  7. +2 −1 ma_statement.h
  8. +3 −8 test/dyn_cursor.c
  9. +6 −6 test/scroll.c
@@ -120,8 +120,6 @@ IF(WIN32 OR WITH_OPENSSL OR "${WITH_SSL}" STREQUAL "OPENSSL")
#ENDIF()

SET(PLATFORM_DEPENDENCIES ${PLATFORM_DEPENDENCIES} ${SSL_LIBRARIES})
# Temporary workarond for the (typo) issue in the c/c 3.1.4
SET(CRYT_LIBS ${SSL_LIBRARIES})
ELSE()
MESSAGE(FATAL_ERROR "OpenSSL not found. Please install OpenSSL or disable SSL support via option -DWITH_OPENSSL=Off")
ENDIF()
@@ -244,9 +244,10 @@ typedef struct
/* TODO: To check is it 0 or 1 based? not quite clear from its usage */
typedef struct
{
char *Name;
SQLLEN Position;
SQLLEN RowsetSize;
char *Name;
SQLLEN Position;
SQLLEN RowsetSize;
MYSQL_ROW_OFFSET Next;
} MADB_Cursor;

enum MADB_DaeType {MADB_DAE_NORMAL=0, MADB_DAE_ADD=1, MADB_DAE_UPDATE=2, MADB_DAE_DELETE=3};
@@ -34,6 +34,50 @@ void MADB_StmtResetResultStructures(MADB_Stmt *Stmt)
}
/* }}} */

/* {{{ MoveNext - moves C/C cursor forward for Offset positions */
SQLRETURN MoveNext(MADB_Stmt *Stmt, unsigned long long Offset)
{
SQLRETURN result= SQL_SUCCESS;

if (Stmt->result != NULL)
{
unsigned int i;
char *SavedFlag;

SavedFlag= (char*)MADB_CALLOC(mysql_stmt_field_count(Stmt->stmt));

if (SavedFlag == NULL)
{
return SQL_ERROR;
}

for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++)
{
SavedFlag[i]= Stmt->stmt->bind[i].flags & MADB_BIND_DUMMY;

Stmt->stmt->bind[i].flags|= MADB_BIND_DUMMY;
}

while (Offset--)
{
if (mysql_stmt_fetch(Stmt->stmt) == 1)
{
result= SQL_ERROR;
break;
}
}

for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++)
{
Stmt->stmt->bind[i].flags &= (~MADB_BIND_DUMMY | SavedFlag[i]);
}

MADB_FREE(SavedFlag);
}
return result;
}
/* }}} */

/* {{{ MADB_StmtDataSeek */
SQLRETURN MADB_StmtDataSeek(MADB_Stmt *Stmt, my_ulonglong FetchOffset)
{
@@ -20,6 +20,7 @@
#define _ma_result_h_

void MADB_StmtResetResultStructures(MADB_Stmt *Stmt);
SQLRETURN MoveNext(MADB_Stmt *Stmt, unsigned long long Offset);
SQLRETURN MADB_StmtDataSeek (MADB_Stmt *Stmt, my_ulonglong FetchOffset);
SQLRETURN MADB_StmtMoreResults(MADB_Stmt *Stmt);
SQLULEN MADB_RowsToFetch(MADB_Cursor *Cursor, SQLULEN ArraySize, unsigned long long RowsInResultst);
@@ -1967,7 +1967,7 @@ else if (_cur_row_rc != _accumulated_rc) _accumulated_rc= SQL_SUCCESS_WITH_INFO
/* {{{ MADB_StmtFetch */
SQLRETURN MADB_StmtFetch(MADB_Stmt *Stmt)
{
unsigned int row_num, j, rc;
unsigned int RowNum, j, rc;
SQLULEN Rows2Fetch= Stmt->Ard->Header.ArraySize, Processed, *ProcessedPtr= &Processed;
MYSQL_ROW_OFFSET SaveCursor= NULL;
SQLRETURN Result= SQL_SUCCESS, RowResult;
@@ -1986,9 +1986,6 @@ SQLRETURN MADB_StmtFetch(MADB_Stmt *Stmt)
return Stmt->Error.ReturnValue;
}

/* We don't know if any of the ARD parameter changed, so we need to rebind */
//MADB_FREE(Stmt->result);

/* We don't have much to do if ArraySize == 0 */
if (Stmt->Ard->Header.ArraySize == 0)
{
@@ -2030,30 +2027,31 @@ SQLRETURN MADB_StmtFetch(MADB_Stmt *Stmt)
{
SaveCursor= mysql_stmt_row_tell(Stmt->stmt);
/* Skipping current row for for reading now, it will be read when the Cursor is returned to it */
MADB_StmtDataSeek(Stmt, Stmt->Cursor.Position > 0 ? Stmt->Cursor.Position + 1 : 1);
MoveNext(Stmt, 1LL);
}

for (j= 0; j < Rows2Fetch; ++j)
{
RowResult= SQL_SUCCESS;
/* If we need to return to cursor to 1st row in the rowset, we start to read it from 2nd, and 1st row we read the last */
/* If we need to return the cursor to 1st row in the rowset, we start to read it from 2nd, and 1st row we read the last */
if (SaveCursor != NULL)
{
row_num= j + 1;
if (row_num == Rows2Fetch)
RowNum= j + 1;
if (RowNum == Rows2Fetch)
{
row_num= 0;
RowNum= 0;
Stmt->Cursor.Next= mysql_stmt_row_tell(Stmt->stmt);
mysql_stmt_row_seek(Stmt->stmt, SaveCursor);
}
}
else
{
row_num= j;
RowNum= j;
}
/*************** Setting up BIND structures ********************/
/* Basically, nothing should happen here, but if happens, then it will happen on each row.
Thus it's ok to stop */
RETURN_ERROR_OR_CONTINUE(MADB_PrepareBind(Stmt, row_num));
RETURN_ERROR_OR_CONTINUE(MADB_PrepareBind(Stmt, RowNum));

/************************ Bind! ********************************/
mysql_stmt_bind_result(Stmt->stmt, Stmt->result);
@@ -2062,7 +2060,7 @@ SQLRETURN MADB_StmtFetch(MADB_Stmt *Stmt)
{
/* TODO: Bookmark can be not only "unsigned long*", but also "unsigned char*". Can be determined by examining Stmt->Options.BookmarkType */
long *p= (long *)Stmt->Options.BookmarkPtr;
p+= row_num * Stmt->Options.BookmarkLength;
p+= RowNum * Stmt->Options.BookmarkLength;
*p= (long)Stmt->Cursor.Position;
}
/************************ Fetch! ********************************/
@@ -2081,9 +2079,9 @@ SQLRETURN MADB_StmtFetch(MADB_Stmt *Stmt)
/* If mysql_stmt_fetch returned error, there is no sense to continue */
if (Stmt->Ird->Header.ArrayStatusPtr)
{
Stmt->Ird->Header.ArrayStatusPtr[row_num]= MADB_MapToRowStatus(RowResult);
Stmt->Ird->Header.ArrayStatusPtr[RowNum]= MADB_MapToRowStatus(RowResult);
}
CALC_ALL_ROWS_RC(Result, RowResult, row_num);
CALC_ALL_ROWS_RC(Result, RowResult, RowNum);
return Result;

case MYSQL_DATA_TRUNCATED:
@@ -2101,7 +2099,7 @@ SQLRETURN MADB_StmtFetch(MADB_Stmt *Stmt)
/* If (numeric) field value and buffer are of the same size - ignoring truncation.
In some cases specs are not clear enough if certain column signed or not(think of catalog functions for example), and
some apps bind signed buffer where we return unsigdned value. And in general - if application want to fetch unsigned as
signed, or vice versa, why we should prevent that. Plus it seems there is the bug in C/C atm */
signed, or vice versa, why we should prevent that. */
if (ArdRec->OctetLength == IrdRec->OctetLength
&& MADB_IsIntType(IrdRec->ConciseType) && MADB_IsIntType(ArdRec->ConciseType))
{
@@ -2122,7 +2120,7 @@ SQLRETURN MADB_StmtFetch(MADB_Stmt *Stmt)
/* We have already incremented this counter, since there was no more rows, need to decrement */
--*ProcessedPtr;
/* SQL_NO_DATA should be only returned if first fetched row is already beyond end of the resultset */
if (row_num > 0)
if (RowNum > 0)
{
continue;
}
@@ -2133,7 +2131,7 @@ SQLRETURN MADB_StmtFetch(MADB_Stmt *Stmt)
++Stmt->PositionedCursor;

/*Conversion etc. At this point, after fetch we can have RowResult either SQL_SUCCESS or SQL_SUCCESS_WITH_INFO */
switch (MADB_FixFetchedValues(Stmt, row_num, SaveCursor))
switch (MADB_FixFetchedValues(Stmt, RowNum, SaveCursor))
{
case SQL_ERROR:
RowResult= SQL_ERROR;
@@ -2143,11 +2141,11 @@ SQLRETURN MADB_StmtFetch(MADB_Stmt *Stmt)
/* And if result of conversions - success, just leaving that we had before */
}

CALC_ALL_ROWS_RC(Result, RowResult, row_num);
CALC_ALL_ROWS_RC(Result, RowResult, RowNum);

if (Stmt->Ird->Header.ArrayStatusPtr)
{
Stmt->Ird->Header.ArrayStatusPtr[row_num]= MADB_MapToRowStatus(RowResult);
Stmt->Ird->Header.ArrayStatusPtr[RowNum]= MADB_MapToRowStatus(RowResult);
}
}

@@ -4069,43 +4067,10 @@ SQLRETURN MADB_GetCursorName(MADB_Stmt *Stmt, void *CursorName, SQLSMALLINT Buff
}
/* }}} */

/* {{{ MADB_RefreshRowPtrs */
SQLRETURN MADB_RefreshRowPtrs(MADB_Stmt *Stmt)
{
SQLRETURN result= SQL_SUCCESS;

if (Stmt->result != NULL)
{
unsigned int i;
char *saved_flag;

saved_flag= (char*)MADB_CALLOC(mysql_stmt_field_count(Stmt->stmt));

if (saved_flag == NULL)
{
return SQL_ERROR;
}

for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++)
{
saved_flag[i]= Stmt->stmt->bind[i].flags & MADB_BIND_DUMMY;

Stmt->stmt->bind[i].flags|= MADB_BIND_DUMMY;
}

if (mysql_stmt_fetch(Stmt->stmt) == 1)
{
result= SQL_ERROR;
}

for (i=0; i < mysql_stmt_field_count(Stmt->stmt); i++)
{
Stmt->stmt->bind[i].flags &= (~MADB_BIND_DUMMY | saved_flag[i]);
}

MADB_FREE(saved_flag);
}

return result;
return MoveNext(Stmt, 1LL);
}

/* {{{ MADB_RefreshDynamicCursor */
@@ -4127,21 +4092,7 @@ SQLRETURN MADB_RefreshDynamicCursor(MADB_Stmt *Stmt)
Stmt->LastRowFetched= LastRowFetched;
Stmt->AffectedRows= AffectedRows;

if (Stmt->Cursor.Position > 0)
{
/* Looks likt this is not needed altogether. Leaving it commented out, so far */
/*MADB_StmtDataSeek(Stmt, Stmt->Cursor.Position);
if (SQL_SUCCEEDED(ret))
{*/
/* We need to prevent that bound variables will be overwritten
by fetching data again: For subsequent GetData we need to update
bind->row_ptr */
/*Stmt->Methods->RefreshRowPtrs(Stmt);
MADB_StmtDataSeek(Stmt, Stmt->Cursor.Position);
}*/
}
else
if (Stmt->Cursor.Position < 0)
{
Stmt->Cursor.Position= 0;
}
@@ -4334,7 +4285,7 @@ SQLRETURN MADB_StmtSetPos(MADB_Stmt *Stmt, SQLSETPOSIROW RowNumber, SQLUSMALLINT
while (Start <= End)
{
SQLSMALLINT param= 0, column;
MADB_StmtDataSeek(Stmt,Start);
MADB_StmtDataSeek(Stmt, Start);
Stmt->Methods->RefreshRowPtrs(Stmt);

/* We don't need to prepare the statement, if SetPos was called
@@ -4493,7 +4444,9 @@ SQLRETURN MADB_StmtSetPos(MADB_Stmt *Stmt, SQLSETPOSIROW RowNumber, SQLUSMALLINT
Stmt->Ard->Header.ArraySize= SaveArraySize;
/* if we have a dynamic cursor we need to adjust the rowset size */
if (Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC)
{
Stmt->LastRowFetched-= (unsigned long)Stmt->AffectedRows;
}
}
break;
case SQL_REFRESH:
@@ -4535,20 +4488,18 @@ SQLRETURN MADB_StmtFetchScroll(MADB_Stmt *Stmt, SQLSMALLINT FetchOrientation,
return Stmt->Error.ReturnValue;
}
}

if (FetchOrientation != SQL_FETCH_NEXT)
{
MADB_STMT_FORGET_NEXT_POS(Stmt);
}

switch(FetchOrientation) {
case SQL_FETCH_NEXT:
Position= Stmt->Cursor.Position < 0 ? 0 : Stmt->Cursor.Position + RowsProcessed;
/* if (Stmt->Ird->Header.RowsProcessedPtr)
Position+= MAX(1, *Stmt->Ird->Header.RowsProcessedPtr);
else
Position++; */
break;
case SQL_FETCH_PRIOR:
Position= Stmt->Cursor.Position < 0 ? - 1: Stmt->Cursor.Position - MAX(1, Stmt->Ard->Header.ArraySize);
/* if (Stmt->Ird->Header.RowsProcessedPtr)
Position-= MAX(1, *Stmt->Ird->Header.RowsProcessedPtr);
else
Position--; */
break;
case SQL_FETCH_RELATIVE:
Position= Stmt->Cursor.Position + FetchOffset;
@@ -4616,9 +4567,21 @@ SQLRETURN MADB_StmtFetchScroll(MADB_Stmt *Stmt, SQLSMALLINT FetchOrientation,
return SQL_NO_DATA;
}

if (FetchOrientation != SQL_FETCH_NEXT || RowsProcessed > 1 || Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC)
/* For dynamic cursor we "refresh" resultset eachtime(basically re-executing), and thus the (c/c)cursor is before 1st row at this point,
and thux we need to restore the last position. For array fetch with not forward_only cursor, the (c/c)cursor is at 1st row of the last
fetched rowset */
if (FetchOrientation != SQL_FETCH_NEXT || (RowsProcessed > 1 && Stmt->Options.CursorType != SQL_CURSOR_FORWARD_ONLY) ||
Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC)
{
ret= MADB_StmtDataSeek(Stmt, Stmt->Cursor.Position);
if (Stmt->Cursor.Next != NULL)
{
mysql_stmt_row_seek(Stmt->stmt, Stmt->Cursor.Next);
ret= SQL_SUCCESS;
}
else
{
ret= MADB_StmtDataSeek(Stmt, Stmt->Cursor.Position);
}
}

/* Assuming, that ret before previous "if" was SQL_SUCCESS */
@@ -118,7 +118,8 @@ SQLRETURN MADB_DoExecute(MADB_Stmt *Stmt, BOOL ExecDirect);
#define MADB_POSITIONED_COMMAND(aStmt) ((aStmt)->PositionedCommand && (aStmt)->PositionedCursor)
/* So far we always use all fields for index. Once that is changed, this should be changed as well */
#define MADB_POS_COMM_IDX_FIELD_COUNT(aStmt) MADB_STMT_COLUMN_COUNT((aStmt)->PositionedCursor)
#define MADB_STMT_RESET_CURSOR(aStmt) (aStmt)->Cursor.Position= -1;
#define MADB_STMT_FORGET_NEXT_POS(aStmt) (aStmt)->Cursor.Next= NULL
#define MADB_STMT_RESET_CURSOR(aStmt) (aStmt)->Cursor.Position= -1; MADB_STMT_FORGET_NEXT_POS(aStmt)
#define MADB_STMT_CLOSE_STMT(aStmt) mysql_stmt_close((aStmt)->stmt);(aStmt)->stmt= NULL

#define MADB_OCTETS_PER_CHAR 2
@@ -354,14 +354,9 @@ ODBC_TEST(my_position)
OK_SIMPLE_STMT(Stmt, "select * from my_position");
CHECK_STMT_RC(Stmt,rc);

rc = SQLFetch(Stmt);
CHECK_STMT_RC(Stmt,rc);

rc = SQLFetch(Stmt);
CHECK_STMT_RC(Stmt,rc);

rc = SQLFetch(Stmt);
CHECK_STMT_RC(Stmt,rc);
CHECK_STMT_RC(Stmt, SQLFetch(Stmt));
CHECK_STMT_RC(Stmt, SQLFetch(Stmt));
CHECK_STMT_RC(Stmt, SQLFetch(Stmt));

rc = SQLGetData(Stmt,1,SQL_C_LONG,&nData,0,NULL);
CHECK_STMT_RC(Stmt,rc);
@@ -164,21 +164,21 @@ ODBC_TEST(t_array_relative_10)

diag("1-10, total rows:%ld\n",(long)nrows);

for (index=1; index<=nrows; index++)
for (index= 0; index < nrows; index++)
{
diag(" %d ",iarray[index-1]);
IS(iarray[index-1] == index);
diag("%d %d ", index, iarray[index]);
is_num(iarray[index], index + 1);
}

rc = SQLFetchScroll(Stmt,SQL_FETCH_NEXT,0);/* 10-20 */
CHECK_STMT_RC(Stmt,rc);

diag("\n10-20, total rows:%ld\n",(long)nrows);

for (index=1; index<=nrows; index++)
for (index= 0; index < nrows; index++)
{
diag(" %d ",iarray[index-1]);
IS(iarray[index-1] == index+10);
diag("%d %d ", index, iarray[index]);
is_num(iarray[index], index + 1 + 10);
}

rc = SQLFetchScroll(Stmt,SQL_FETCH_PREV,0);/* 1-10 */

0 comments on commit f098cc9

Please sign in to comment.
You can’t perform that action at this time.