Skip to content

Commit 71a93d9

Browse files
committed
ODBC-481 Crash in SQLDisconnect after SQLCancel if it only closed cursor
One of locks stayed locked and it is not re-entrant. Added testcase in separate suite as first I expected other conditions would be needed to be met in order to re-create the issue - I thought it would need "clean" and only env. But let it stay separate as that suite may get another use.
1 parent 6ef966f commit 71a93d9

File tree

5 files changed

+125
-4
lines changed

5 files changed

+125
-4
lines changed

driver/ma_api_internal.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -317,6 +317,7 @@ SQLRETURN MA_SQLCancel(SQLHSTMT StatementHandle)
317317
{
318318
ret= MADB_FromException(Stmt->Error, e);
319319
}
320+
lock.unlock();
320321
}
321322
else
322323
{
@@ -354,6 +355,7 @@ SQLRETURN MA_SQLCancel(SQLHSTMT StatementHandle)
354355
{
355356
ret= MADB_FromException(Stmt->Error, e);
356357
}
358+
lock.unlock();
357359
}
358360
else
359361
{

driver/ma_connection.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,7 @@ SQLRETURN MADB_SQLDisconnect(SQLHDBC ConnectionHandle)
172172
}
173173
else
174174
{
175-
MADB_SetError(&Connection->Error, MADB_ERR_08003, nullptr, 0);
176-
ret= Connection->Error.ReturnValue;
175+
ret= MADB_SetError(&Connection->Error, MADB_ERR_08003, nullptr, 0);
177176
}
178177
Connection->ConnOrSrcCharset= nullptr;
179178

test/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ SET (ODBC_TESTS
6060
"basic" "types" "blob" "desc" "info" "tran" "catalog1" "catalog2"
6161
"use_result" "scroll" "bulk" "prepare" "datetime" "keys"
6262
"curext" "relative" "unicode" "cursor" "dyn_cursor"
63-
"error" "param" "result1" "result2" "multistatement" "mariadb_specific")
63+
"error" "param" "result1" "result2" "multistatement" "mariadb_specific" "standalone")
6464
SET(COMMON_TEST_SOURCES tap.h)
6565
# Interactive makes sense on WIN32 only atm
6666
IF (BUILD_INTERACTIVE_TESTS)

test/standalone.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// SPDX-License-Identifier: LGPL-2.1-or-later
2+
// Copyright (c) 2025 MariaDB Corporation plc
3+
4+
// Tests without any automatic ODBC actions from the framework
5+
#include "tap.h"
6+
7+
ODBC_TEST(odbc481)
8+
{
9+
AllocEnvConn(&Env, &Connection);
10+
Stmt= DoConnect(Connection, FALSE, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL);
11+
FAIL_IF(Stmt == NULL, "Failed to connect or allocate statement");
12+
OK_SIMPLE_STMT(Stmt, "SET SESSION lc_messages='en_US'");
13+
EXPECT_STMT(Stmt, SQLMoreResults(Stmt), SQL_NO_DATA_FOUND);
14+
CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));
15+
CHECK_STMT_RC(Stmt, SQLCancel(Stmt));
16+
CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_DROP));
17+
CHECK_DBC_RC(Connection, SQLDisconnect(Connection));
18+
return OK;
19+
}
20+
21+
MA_ODBC_TESTS my_tests[]=
22+
{
23+
{odbc481, "odbc481-onecallcancel", NORMAL},
24+
{NULL, NULL, 0}
25+
};
26+
27+
28+
int main(int argc, char **argv)
29+
{
30+
int ret, tests= sizeof(my_tests)/sizeof(MA_ODBC_TESTS) - 1;
31+
32+
get_options(argc, argv);
33+
34+
plan(tests);
35+
36+
ret= run_tests_bare(my_tests);
37+
38+
return ret;
39+
}

test/tap.h

Lines changed: 82 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ static unsigned long defaultOptions= 67108866;
207207
static unsigned long my_options= 67108866;
208208
#define CHANGE_DEFAULT_OPTIONS(_NEW_OPTIONS) my_options=defaultOptions=_NEW_OPTIONS
209209

210-
static SQLHANDLE Env, Connection, Stmt, wConnection, wStmt;
210+
static SQLHANDLE Env= NULL, Connection= NULL, Stmt= NULL, wConnection= NULL, wStmt= NULL;
211211
static SQLINTEGER OdbcVer= SQL_OV_ODBC3;
212212
static unsigned int DmMajor= 0, DmMinor= 0, DmPatch= 0;
213213
static unsigned int my_port= 3306;
@@ -1189,6 +1189,87 @@ int ReadInfoOneTime(HDBC Connection, HSTMT Stmt)
11891189
return OK;
11901190
}
11911191

1192+
1193+
int run_tests_bare(MA_ODBC_TESTS* tests)
1194+
{
1195+
int rc, i= 1, failed= 0;
1196+
const char* comment;
1197+
SQLWCHAR* buff_before_test;
1198+
1199+
utf16= (little_endian() ? &utf16le : &utf16be);
1200+
utf32= (little_endian() ? &utf32le : &utf32be);
1201+
1202+
if (sizeof(SQLWCHAR) == 4)
1203+
{
1204+
DmUnicode= utf32;
1205+
}
1206+
else
1207+
{
1208+
DmUnicode= utf16;
1209+
}
1210+
1211+
if (utf8 == NULL || utf16 == NULL || utf32 == NULL)
1212+
{
1213+
fprintf(stdout, "HALT! Could not load charset info %p %p %p\n", utf8, utf16, utf32);
1214+
return 1;
1215+
}
1216+
1217+
wdsn= str2sqlwchar_on_gbuff((const char*)my_dsn, strlen((const char*)my_dsn) + 1, utf8, DmUnicode);
1218+
wuid= str2sqlwchar_on_gbuff((const char*)my_uid, strlen((const char*)my_uid) + 1, utf8, DmUnicode);
1219+
wpwd= str2sqlwchar_on_gbuff((const char*)my_pwd, strlen((const char*)my_pwd) + 1, utf8, DmUnicode);
1220+
wschema= str2sqlwchar_on_gbuff((const char*)my_schema, strlen((const char*)my_schema) + 1, utf8, DmUnicode);
1221+
wservername= str2sqlwchar_on_gbuff((const char*)my_servername, strlen((const char*)my_servername) + 1, utf8, DmUnicode);
1222+
wdrivername= str2sqlwchar_on_gbuff((const char*)my_drivername, strlen((const char*)my_drivername) + 1, utf8, DmUnicode);
1223+
wstrport= str2sqlwchar_on_gbuff((const char*)ma_strport, strlen((const char*)ma_strport) + 1, utf8, DmUnicode);
1224+
wadd_connstr= str2sqlwchar_on_gbuff((const char*)add_connstr, strlen((const char*)add_connstr) + 1, utf8, DmUnicode);
1225+
1226+
fprintf(stdout, "1..%d\n", tests_planned);
1227+
while (tests->title)
1228+
{
1229+
buff_before_test= buff_pos;
1230+
1231+
if (tests->skip_condition != NULL && tests->skip_condition())
1232+
{
1233+
rc= SKIP;
1234+
}
1235+
else
1236+
{
1237+
rc= tests->my_test();
1238+
}
1239+
comment = "";
1240+
if (rc != SKIP && tests->test_type != NORMAL)
1241+
{
1242+
comment= comments[tests->test_type][rc];
1243+
rc= OK;
1244+
}
1245+
else if (rc == FAIL)
1246+
{
1247+
failed++;
1248+
}
1249+
1250+
fprintf(stdout, "%s %d - %s%s\n", test_status[rc], i++, tests->title, comment);
1251+
tests++;
1252+
1253+
/* Relieving tests from restoring my_options and/or add_connstr. Also, a test may fail */
1254+
my_options= defaultOptions;
1255+
add_connstr= storedAddConnstr;
1256+
/* reset Statement */
1257+
fflush(stdout);
1258+
}
1259+
1260+
if (wConnection)
1261+
{
1262+
SQLDisconnect(wConnection);
1263+
SQLFreeHandle(SQL_HANDLE_DBC, wConnection);
1264+
}
1265+
ODBC_Disconnect(Env, Connection, Stmt);
1266+
1267+
if (failed)
1268+
return 1;
1269+
return 0;
1270+
}
1271+
1272+
11921273
int run_tests_ex(MA_ODBC_TESTS *tests, BOOL ProvideWConnection)
11931274
{
11941275
int rc, i=1, failed=0;

0 commit comments

Comments
 (0)