Skip to content

Commit b4f3713

Browse files
committed
ODBC-482 Problems with SQLSetPos on block cursor with NULL values.
If position was changed, SQLGetData would still read if value is NULL from first row in the block cursor. That could lead to different issues and even crash on SQL_DELETE or in SQLGetData() after SQL_POSITION. Part of fix is also in the part that has been moved to c/c++ repo - get() method returned true, i.e. error on NULL field value. Plus resultset method isNull has been made public.
1 parent 71a93d9 commit b4f3713

File tree

4 files changed

+56
-47
lines changed

4 files changed

+56
-47
lines changed

driver/ma_statement.cpp

Lines changed: 23 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2756,8 +2756,8 @@ SQLRETURN MADB_StmtGetData(SQLHSTMT StatementHandle,
27562756
{
27572757
return MADB_SetError(&Stmt->Error, MADB_ERR_HY109, nullptr, 0);
27582758
}
2759-
/* Will it be set with all dummies? */
2760-
if (Stmt->result[Offset].is_null && *Stmt->result[Offset].is_null != '\0')
2759+
2760+
if (Stmt->rs->isNull(Col_or_Param_Num))
27612761
{
27622762
if (!StrLen_or_IndPtr)
27632763
{
@@ -2985,6 +2985,10 @@ SQLRETURN MADB_StmtGetData(SQLHSTMT StatementHandle,
29852985
Bind.length= &Bind.length_value;
29862986

29872987
Stmt->rs->get(&Bind, Offset, Stmt->CharOffset[Offset]);
2988+
//Should not happen
2989+
if (IsNull) {
2990+
break;
2991+
}
29882992

29892993
if (InternalUse)
29902994
{
@@ -2998,16 +3002,16 @@ SQLRETURN MADB_StmtGetData(SQLHSTMT StatementHandle,
29983002
}
29993003
*StrLen_or_IndPtr= Stmt->Lengths[Offset] - Stmt->CharOffset[Offset];
30003004
}
3001-
3002-
MADB_SetError(&Stmt->Error, MADB_ERR_01004, nullptr, 0);
3003-
3004-
return SQL_SUCCESS_WITH_INFO;
3005+
return MADB_SetError(&Stmt->Error, MADB_ERR_01004, nullptr, 0);
30053006
}
30063007

30073008
if (Stmt->rs->get(&Bind, Offset, CurrentOffset))
30083009
{
3009-
MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->stmt.get());
3010-
return Stmt->Error.ReturnValue;
3010+
return MADB_SetNativeError(&Stmt->Error, SQL_HANDLE_STMT, Stmt->stmt.get());
3011+
}
3012+
// Should not happen
3013+
if (IsNull) {
3014+
break;
30113015
}
30123016
/* Dirty temporary hack before we know what is going on. Yes, there is nothing more eternal, than temporary
30133017
It's not that bad, after all */
@@ -3160,6 +3164,14 @@ SQLRETURN MADB_StmtGetData(SQLHSTMT StatementHandle,
31603164
return MADB_SetError(&Stmt->Error, MADB_ERR_22003, oor.what(), 0);
31613165
}
31623166

3167+
if (IsNull)
3168+
{
3169+
if (!StrLen_or_IndPtr)
3170+
{
3171+
return MADB_SetError(&Stmt->Error, MADB_ERR_22002, nullptr, 0);
3172+
}
3173+
*StrLen_or_IndPtr = SQL_NULL_DATA;
3174+
}
31633175
/* Marking fixed length fields to be able to return SQL_NO_DATA on subsequent calls, as standard prescribes
31643176
"SQLGetData cannot be used to return fixed-length data in parts. If SQLGetData is called more than one time
31653177
in a row for a column containing fixed-length data, it returns SQL_NO_DATA for all calls after the first."
@@ -3168,16 +3180,6 @@ SQLRETURN MADB_StmtGetData(SQLHSTMT StatementHandle,
31683180
{
31693181
Stmt->CharOffset[Offset]= MAX((unsigned long)Bind.buffer_length, Bind.length_value);
31703182
}
3171-
3172-
if (IsNull)
3173-
{
3174-
if (!StrLen_or_IndPtr)
3175-
{
3176-
return MADB_SetError(&Stmt->Error, MADB_ERR_22002, nullptr, 0);
3177-
}
3178-
*StrLen_or_IndPtr= SQL_NULL_DATA;
3179-
}
3180-
31813183
return Stmt->Error.ReturnValue;
31823184
}
31833185
/* }}} */
@@ -3618,8 +3620,8 @@ SQLRETURN MADB_StmtSetPos(MADB_Stmt* Stmt, SQLSETPOSIROW RowNumber, SQLUSMALLINT
36183620
switch (Operation) {
36193621
case SQL_POSITION:
36203622
{
3621-
Stmt->Cursor.Position += (RowNumber - 1);
3622-
MADB_StmtDataSeek(Stmt, Stmt->Cursor.Position);
3623+
//Stmt->Cursor.Position += (RowNumber - 1);
3624+
MADB_StmtDataSeek(Stmt, Stmt->Cursor.Position + RowNumber - 1);
36233625
}
36243626
case SQL_REFRESH:
36253627
/* todo */
@@ -3887,6 +3889,7 @@ SQLRETURN MADB_StmtSetPos(MADB_Stmt* Stmt, SQLSETPOSIROW RowNumber, SQLUSMALLINT
38873889
std::lock_guard<std::mutex> localScopeLock(Stmt->Connection->guard->getLock());
38883890
Stmt->Connection->guard->safeRealQuery(DynamicStmt);
38893891
Stmt->AffectedRows += mysql_affected_rows(Stmt->Connection->mariadb);
3892+
MADB_SETPOS_AGG_RESULT(result, SQL_SUCCESS);
38903893
}
38913894
++Start;
38923895
DynamicStmt.erase(baseStmtLen);

test/basic.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2184,7 +2184,7 @@ ODBC_TEST(t_auto_reconnect)
21842184
SQLHDBC Hdbc;
21852185
SQLHSTMT Hstmt;
21862186

2187-
//SKIPIF(IsMaxScale || IsSkySqlHa, "Doesn't make sense with Maxscale, as we kill connection from MaxScale to one of servers, and our connection to MaxScale persists");
2187+
SKIPIF(IsMaxScale || IsSkySqlHa, "Doesn't make sense with Maxscale, as we kill connection from MaxScale to one of servers, and our connection to MaxScale persists");
21882188
/* Create a new connection with reconnect option on that we will kill to check if it still functioning */
21892189
CHECK_ENV_RC(Env, SQLAllocConnect(Env, &Hdbc));
21902190
Hstmt = DoConnect(Hdbc, FALSE, NULL, NULL, NULL, 0, NULL, NULL, NULL, "AUTO_RECONNECT=1");

test/dyn_cursor.c

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -802,30 +802,36 @@ ODBC_TEST(odbc476)
802802

803803
FAIL_IF(SQLFetchScroll(Stmt, SQL_FETCH_NEXT, 1) != SQL_NO_DATA_FOUND, "no data found expected");
804804

805-
// The commented does not work, but it never did it seems. So, leaving it so far
806-
//CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_UNBIND));
807-
//CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));
808-
809-
///* Now the same but on block cursor. Not null values should be deleted, SQLSetPos should return SQL_SUCCESS_WITH_INFO */
810-
//OK_SIMPLE_STMT(Stmt, "SELECT string_null FROM t_odbc476 ORDER BY string_null DESC");
811-
//CHECK_STMT_RC(Stmt, SQLBindCol(Stmt, 1, SQL_C_CHAR, szData, sizeof(szData[0]), nlen));
812-
//CHECK_STMT_RC(Stmt, SQLFetchScroll(Stmt, SQL_FETCH_ABSOLUTE, 1));
813-
//// This fails with "can't build index" if id field or all fields are not selected
814-
//EXPECT_STMT(Stmt, SQLSetPos(Stmt, 0, SQL_DELETE, SQL_LOCK_NO_CHANGE), SQL_SUCCESS_WITH_INFO);
815-
816-
//CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));
817-
818-
//OK_SIMPLE_STMT(Stmt, "SELECT * FROM t_odbc476 ORDER BY id");
819-
//CHECK_STMT_RC(Stmt, SQLSetStmtAttr(Stmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)2, 0));
820-
//CHECK_STMT_RC(Stmt, SQLBindCol(Stmt, 1, SQL_C_LONG, &nData, 0, nrow));
821-
//CHECK_STMT_RC(Stmt, SQLBindCol(Stmt, 2, SQL_C_CHAR, szData, sizeof(szData[0]), nlen));
822-
//CHECK_STMT_RC(Stmt, SQLFetchScroll(Stmt, SQL_FETCH_ABSOLUTE, 1));
823-
824-
//is_num(nData[0], 1);
825-
//is_num(nlen[0], SQL_NULL_DATA);
826-
//is_num(nData[1], 4);
827-
//is_num(nlen[1], SQL_NULL_DATA);
828-
//FAIL_IF(SQLFetchScroll(Stmt, SQL_FETCH_NEXT, 1) != SQL_NO_DATA_FOUND, "no data found expected");
805+
CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_UNBIND));
806+
CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));
807+
808+
/* Now the same but on block cursor. Not null values should be deleted, SQLSetPos should return SQL_SUCCESS_WITH_INFO */
809+
OK_SIMPLE_STMT(Stmt, "SELECT string_null FROM t_odbc476 ORDER BY string_null DESC"); // To make NULL go last
810+
CHECK_STMT_RC(Stmt, SQLBindCol(Stmt, 1, SQL_C_CHAR, szData, sizeof(szData[0]), nlen));
811+
CHECK_STMT_RC(Stmt, SQLFetchScroll(Stmt, SQL_FETCH_ABSOLUTE, 1));
812+
813+
// Setting position to first NULL value. From here this is also and mostly test for ODBC-482
814+
CHECK_STMT_RC(Stmt, SQLSetPos(Stmt, 3, SQL_POSITION, SQL_LOCK_NO_CHANGE));
815+
// Now we should be able to SQLGetData from that row. Gonna use the same buffer
816+
nlen[2]= 0;
817+
CHECK_STMT_RC(Stmt, SQLGetData(Stmt, 1, SQL_C_CHAR, &szData[2], sizeof(szData[2]), &nlen[2]));
818+
is_num(SQL_NULL_DATA, nlen[2]);
819+
// This fails with "can't build index" if id field or all fields are not selected
820+
EXPECT_STMT(Stmt, SQLSetPos(Stmt, 0, SQL_DELETE, SQL_LOCK_NO_CHANGE), SQL_SUCCESS_WITH_INFO);
821+
822+
CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));
823+
824+
OK_SIMPLE_STMT(Stmt, "SELECT * FROM t_odbc476 ORDER BY id");
825+
CHECK_STMT_RC(Stmt, SQLSetStmtAttr(Stmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)2, 0));
826+
CHECK_STMT_RC(Stmt, SQLBindCol(Stmt, 1, SQL_C_LONG, &nData, 0, nrow));
827+
CHECK_STMT_RC(Stmt, SQLBindCol(Stmt, 2, SQL_C_CHAR, szData, sizeof(szData[0]), nlen));
828+
CHECK_STMT_RC(Stmt, SQLFetchScroll(Stmt, SQL_FETCH_ABSOLUTE, 1));
829+
830+
is_num(nData[0], 1);
831+
is_num(nlen[0], SQL_NULL_DATA);
832+
is_num(nData[1], 4);
833+
is_num(nlen[1], SQL_NULL_DATA);
834+
FAIL_IF(SQLFetchScroll(Stmt, SQL_FETCH_NEXT, 1) != SQL_NO_DATA_FOUND, "no data found expected");
829835

830836
CHECK_STMT_RC(Stmt, SQLSetStmtAttr(Stmt, SQL_ATTR_ROW_ARRAY_SIZE, (SQLPOINTER)1, 0));
831837
CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_UNBIND));
@@ -846,7 +852,7 @@ MA_ODBC_TESTS my_tests[]=
846852
{my_zero_irow_update, "my_zero_irow_update", NORMAL },
847853
{my_zero_irow_delete, "my_zero_irow_delete", NORMAL},
848854
{my_dynamic_cursor, "my_dynamic_cursor", NORMAL},
849-
{odbc476, "odbc476-not-enough-info", NORMAL},
855+
{odbc476, "odbc476_odbc482-not-enough-info", NORMAL},
850856
{NULL, NULL}
851857
};
852858

0 commit comments

Comments
 (0)