Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
ODBC-169 The fix and the testcase.
If data was fetched using SQLGetData, with batches of SELECTS that would fail like described int the bug - empty values or even crashes. The reason was that in such case one of structures involved in data fetching was not reset on move to the new resultset. That caused aforementioned errors.
Also the patch fixes SQLRowsCount for batches of upserts or other
statements generation affected rows count.
Removed one unused funxtion, which also relied on C/C internals
  • Loading branch information
lawrinn committed Sep 13, 2018
1 parent 79efd0e commit bfb78c0
Show file tree
Hide file tree
Showing 6 changed files with 146 additions and 26 deletions.
14 changes: 1 addition & 13 deletions ma_helper.c
Expand Up @@ -1067,20 +1067,7 @@ size_t MADB_GetHexString(char *BinaryBuffer, size_t BinaryLength,
*HexBuffer= 0;
return (HexBuffer - Start);
}
/* }}} */

unsigned long MADB_StmtDataTell(MADB_Stmt *Stmt)
{
MYSQL_ROWS *ptr= Stmt->stmt->result.data;
unsigned long Offset= 0;

while (ptr && ptr!= Stmt->stmt->result_cursor)
{
Offset++;
ptr= ptr->next;
}
return ptr ? Offset : 0;
}

SQLRETURN MADB_DaeStmt(MADB_Stmt *Stmt, SQLUSMALLINT Operation)
{
Expand Down Expand Up @@ -1237,6 +1224,7 @@ void MADB_InstallStmt(MADB_Stmt *Stmt, MYSQL_STMT *stmt)
}
else
{
Stmt->AffectedRows= 0;
MADB_StmtResetResultStructures(Stmt);
MADB_DescSetIrdMetadata(Stmt, mysql_fetch_fields(FetchMetadata(Stmt)), mysql_stmt_field_count(Stmt->stmt));
}
Expand Down
1 change: 0 additions & 1 deletion ma_helper.h
Expand Up @@ -65,7 +65,6 @@ int MADB_CharToSQLNumeric (char *buffer, MADB_Desc *Ard, MADB_DescReco
SQL_NUMERIC_STRUCT *dst_buffer, unsigned long RowNumber);
void MADB_NumericInit (SQL_NUMERIC_STRUCT *number, MADB_DescRecord *Ard);

unsigned long MADB_StmtDataTell (MADB_Stmt *Stmt);
int MADB_FindNextDaeParam (MADB_Desc *Desc, int InitialParam, SQLSMALLINT RowNumber);

BOOL MADB_IsNumericType(SQLSMALLINT ConciseType);
Expand Down
17 changes: 14 additions & 3 deletions ma_result.c
Expand Up @@ -54,11 +54,16 @@ SQLRETURN MADB_StmtDataSeek(MADB_Stmt *Stmt, my_ulonglong FetchOffset)
SQLRETURN MADB_StmtMoreResults(MADB_Stmt *Stmt)
{
SQLRETURN ret= SQL_SUCCESS;

if (!Stmt->stmt)
{
return MADB_SetError(&Stmt->Error, MADB_ERR_08S01, NULL, 0);
}

/* We can't have it in MADB_StmtResetResultStructures, as it breaks dyn_cursor functionality.
Thus we free-ing bind structs on move to new result only */
MADB_FREE(Stmt->result);

if (Stmt->MultiStmts)
{
if (Stmt->MultiStmtNr == STMT_COUNT(Stmt->Query) - 1)
Expand All @@ -82,6 +87,14 @@ SQLRETURN MADB_StmtMoreResults(MADB_Stmt *Stmt)
{
LOCK_MARIADB(Stmt->Connection);
mysql_next_result(Stmt->Connection->mariadb);
if (mysql_field_count(Stmt->Connection->mariadb) != 0)
{
ret= MADB_SetError(&Stmt->Error, MADB_ERR_HY000, "Can't process text result", 0);
}
else
{
Stmt->AffectedRows= mysql_affected_rows(Stmt->Connection->mariadb);
}
UNLOCK_MARIADB(Stmt->Connection);
}
return ret;
Expand All @@ -103,9 +116,6 @@ SQLRETURN MADB_StmtMoreResults(MADB_Stmt *Stmt)
return MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->stmt);
}

/* We can't have it in MADB_StmtResetResultStructures, as it breaks dyn_cursor functionality.
Thus we free-ing bind structs on move to new result only */
MADB_FREE(Stmt->result);
MADB_StmtResetResultStructures(Stmt);

if (mysql_stmt_field_count(Stmt->stmt) == 0)
Expand All @@ -115,6 +125,7 @@ SQLRETURN MADB_StmtMoreResults(MADB_Stmt *Stmt)
else
{
MADB_DescSetIrdMetadata(Stmt, mysql_fetch_fields(FetchMetadata(Stmt)), mysql_stmt_field_count(Stmt->stmt));
Stmt->AffectedRows= 0;

if (Stmt->Connection->mariadb->server_status & SERVER_PS_OUT_PARAMS)
{
Expand Down
3 changes: 1 addition & 2 deletions ma_statement.c
Expand Up @@ -1812,7 +1812,6 @@ SQLRETURN MADB_FixFetchedValues(MADB_Stmt *Stmt, int RowNumber, MYSQL_ROW_OFFSET
Stmt->result[i].buffer = (char *)ArdRec->DataPtr + (RowNumber + 1) * ArdRec->OctetLength;
}
}
// Stmt->result[i].buffer = (char *)Stmt->result[i].buffer + Stmt->result[i].length;
if (IndicatorPtr)
*IndicatorPtr= *Stmt->stmt->bind[i].length;
break;
Expand Down Expand Up @@ -2973,7 +2972,7 @@ SQLRETURN MADB_StmtRowCount(MADB_Stmt *Stmt, SQLLEN *RowCountPtr)
{
if (Stmt->AffectedRows != -1)
*RowCountPtr= (SQLLEN)Stmt->AffectedRows;
else if (Stmt->stmt->result.rows && Stmt->stmt && mysql_stmt_field_count(Stmt->stmt))
else if (Stmt->stmt && Stmt->stmt->result.rows && mysql_stmt_field_count(Stmt->stmt))
*RowCountPtr= (SQLLEN)mysql_stmt_num_rows(Stmt->stmt);
else
*RowCountPtr= 0;
Expand Down
76 changes: 71 additions & 5 deletions test/multistatement.c
Expand Up @@ -33,7 +33,7 @@ ODBC_TEST(test_multi_statements)
OK_SIMPLE_STMT(Stmt, "DROP TABLE IF EXISTS t1");
OK_SIMPLE_STMT(Stmt, "CREATE TABLE t1 (a int)");

OK_SIMPLE_STMT(Stmt, "INSERT INTO t1 VALUES(1);INSERT INTO t1 VALUES(2)");
OK_SIMPLE_STMT(Stmt, "INSERT INTO t1 VALUES(1);INSERT INTO t1 VALUES(2), (3)");

SQLRowCount(Stmt, &num_inserted);
diag("inserted: %ld", (long)num_inserted);
Expand All @@ -42,7 +42,7 @@ ODBC_TEST(test_multi_statements)
rc= SQLMoreResults(Stmt);
num_inserted= 0;
rc= SQLRowCount(Stmt, &num_inserted);
FAIL_IF(num_inserted != 1, "Expected 1 row inserted");
FAIL_IF(num_inserted != 2, "Expected 2 row inserted");

rc= SQLMoreResults(Stmt);
FAIL_IF(rc != SQL_NO_DATA, "expected no more results");
Expand Down Expand Up @@ -194,7 +194,7 @@ ODBC_TEST(test_semicolon)
Also tests ODBC-97*/
ODBC_TEST(t_odbc74)
{
SQLCHAR ref[][4]={"\"", "'", "*/", "/*", "end", "one\\", "two\\"}, val[8];
SQLCHAR ref[][4]= {"\"", "'", "*/", "/*", "end", "one\\", "two\\"}, val[8];
unsigned int i;
SQLHDBC hdbc1;
SQLHSTMT Stmt1;
Expand Down Expand Up @@ -262,7 +262,7 @@ ODBC_TEST(t_odbc95)

ODBC_TEST(t_odbc126)
{
SQLCHAR Query[][24]={ "CALL odbc126_1", "CALL odbc126_2", "SELECT 1, 2; SELECT 3", "SELECT 4; SELECT 5,6" };
SQLCHAR Query[][24]= { "CALL odbc126_1", "CALL odbc126_2", "SELECT 1, 2; SELECT 3", "SELECT 4; SELECT 5,6" };
unsigned int i, ExpectedRows[]= {3, 3, 1, 1}, resCount;
SQLLEN affected;
SQLRETURN rc, Expected= SQL_SUCCESS;
Expand Down Expand Up @@ -378,10 +378,35 @@ ODBC_TEST(diff_column_binding)

ODBC_TEST(t_odbc159)
{
unsigned int j= 0, ExpectedRows[]= {0, 0, 5};
SQLLEN Rows, ExpRowCount[]= {0, 0, 0};
SQLSMALLINT ColumnsCount, expCols[]= {0,0,16};
SQLRETURN rc;

OK_SIMPLE_STMT(Stmt, "DROP TABLE IF EXISTS _temp_odbc159;\
CREATE TEMPORARY TABLE _temp_odbc159 AS(SELECT * FROM INFORMATION_SCHEMA.STATISTICS);\
SELECT * FROM _temp_odbc159;");
SELECT * FROM _temp_odbc159 LIMIT 5;");

do {
CHECK_STMT_RC(Stmt, SQLRowCount(Stmt, &Rows));
if (j == 1)
{
diag("Rows in created table: %lld\n", (long long)Rows);
ExpRowCount[2]= Rows < ExpRowCount[2] ? Rows : ExpRowCount[2];
}
else
{
is_num(Rows, ExpRowCount[j]);
}

CHECK_STMT_RC(Stmt, SQLNumResultCols(Stmt, &ColumnsCount));
is_num(ColumnsCount, expCols[j]);

is_num(ma_print_result_getdata_ex(Stmt, FALSE), ExpectedRows[j]);

rc= SQLMoreResults(Stmt);
++j;
} while (rc != SQL_NO_DATA);

CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));

Expand Down Expand Up @@ -481,6 +506,46 @@ ODBC_TEST(t_odbc177)
}


ODBC_TEST(t_odbc169)
{
SQLCHAR Query[][80]= {"SELECT 1 Col1; SELECT * from t_odbc169", "SELECT * from t_odbc169; SELECT * from t_odbc169 ORDER BY col1 DESC",
"INSERT INTO t_odbc169 VALUES(8, 7, 'Row #4');SELECT * from t_odbc169"};
unsigned int i, j= 0, ExpectedRows[]= {1, 3, 3, 3, 0, 4, 1};
SQLLEN Rows, ExpRowCount[]= {0, 0, 0, 0, 1, 0, 0};
SQLSMALLINT ColumnsCount, expCols[]= {1, 3, 3, 3, 0, 3, 1};
SQLRETURN rc;

OK_SIMPLE_STMT(Stmt, "DROP TABLE IF EXISTS t_odbc169");

OK_SIMPLE_STMT(Stmt, "CREATE TABLE t_odbc169(col1 INT, col2 INT, col3 varchar(32) not null)");

OK_SIMPLE_STMT(Stmt, "INSERT INTO t_odbc169 VALUES(1, 2, 'Row 1'),(3, 4, 'Row 2'), (5, 6, 'Row 3')");

for (i= 0; i < sizeof(Query)/sizeof(Query[0]); ++i)
{
OK_SIMPLE_STMT(Stmt, Query[i]);

do {
CHECK_STMT_RC(Stmt, SQLRowCount(Stmt, &Rows));
is_num(Rows, ExpRowCount[j]);
CHECK_STMT_RC(Stmt, SQLNumResultCols(Stmt, &ColumnsCount));
is_num(ColumnsCount, expCols[j]);

is_num(ma_print_result_getdata_ex(Stmt, FALSE), ExpectedRows[j]);

rc= SQLMoreResults(Stmt);
++j;
} while (rc != SQL_NO_DATA);

CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));
}

OK_SIMPLE_STMT(Stmt, "DROP TABLE t_odbc169");

return OK;
}


MA_ODBC_TESTS my_tests[]=
{
{test_multi_statements, "test_multi_statements"},
Expand All @@ -494,6 +559,7 @@ MA_ODBC_TESTS my_tests[]=
{diff_column_binding, "diff_column_binding"},
{t_odbc159, "t_odbc159"},
{t_odbc177, "t_odbc177"},
{t_odbc169, "t_odbc169"},
{NULL, NULL}
};

Expand Down
61 changes: 59 additions & 2 deletions test/tap.h
Expand Up @@ -443,6 +443,63 @@ int my_print_non_format_result(SQLHSTMT Stmt)
}


/* Same as my_print_non_format_result_ex, but uses SQLGetData, to fetch values, and not bind buffers */
int ma_print_result_getdata_ex(SQLHSTMT Stmt, BOOL CloseCursor)
{
SQLRETURN rc;
SQLUINTEGER nRowCount=0;
SQLULEN pcColDef;
SQLCHAR szColName[MAX_NAME_LEN + 1];
SQLCHAR szData[MAX_ROW_DATA_LEN]= {0};
SQLSMALLINT nIndex, ncol= 0, pfSqlType, pcbScale, pfNullable;
SQLLEN ind_strlen;

rc = SQLNumResultCols(Stmt, &ncol);

mystmt_rows(Stmt, rc, -1);

for (nIndex = 1; nIndex <= ncol; ++nIndex)
{
rc = SQLDescribeCol(Stmt, nIndex, szColName, MAX_NAME_LEN, NULL,
&pfSqlType, &pcColDef, &pcbScale, &pfNullable);
/* Returning in case of an error -nIndex we will see in the log column# */
mystmt_rows(Stmt, rc, -nIndex);

fprintf(stdout, "%s\t", szColName);
}

fprintf(stdout, "\n");

while (SQL_SUCCEEDED(SQLFetch(Stmt)))
{
++nRowCount;
for (nIndex=0; nIndex< ncol; ++nIndex)
{
rc= SQLGetData(Stmt, nIndex + 1, SQL_CHAR, szData, sizeof(szData), &ind_strlen);
mystmt_rows(Stmt, rc, -nIndex);
fprintf(stdout, "%s\t", szData);
}

fprintf(stdout, "\n");
}

if (CloseCursor)
{
SQLFreeStmt(Stmt, SQL_CLOSE);
}

fprintf(stdout, "# Total rows fetched: %d\n", (int)nRowCount);

return nRowCount;
}


int ma_print_result_getdata(SQLHSTMT Stmt)
{
return my_print_non_format_result_ex(Stmt, TRUE);
}


#define OK_SIMPLE_STMT(stmt, stmtstr)\
if (SQLExecDirect((stmt), (SQLCHAR*)(stmtstr), (SQLINTEGER)strlen(stmtstr)) != SQL_SUCCESS)\
{\
Expand Down Expand Up @@ -551,11 +608,11 @@ SQLWCHAR *my_fetch_wstr(SQLHSTMT Stmt, SQLWCHAR *buffer, SQLUSMALLINT icol, SQLL
return buffer;
}

const char *my_fetch_str(SQLHSTMT Stmt, SQLCHAR *szData,SQLUSMALLINT icol)
const char *my_fetch_str(SQLHSTMT Stmt, SQLCHAR *szData, SQLUSMALLINT icol)
{
SQLLEN nLen;

SQLGetData(Stmt,icol,SQL_CHAR,szData,1000,&nLen);
SQLGetData(Stmt, icol, SQL_CHAR, szData, 1000, &nLen);
/* If Null value - putting down smth meaningful. also that allows caller to
better/(in more easy way) test the value */
if (nLen < 0)
Expand Down

0 comments on commit bfb78c0

Please sign in to comment.