Skip to content

Commit 3161979

Browse files
committed
ODBC-473 SQLGetData could write data of the wrong length in case of
resultset streaming and cached resultset. Cached here means - cached in the ODBC driver, not on the client side in general. That caching occurs if not the whole result has been fetched, and other statement is executed. Also contains more fixes of the testcases for the RS streaming and other small fixes. Turned on testing in RS streaming mode in github actions. Added in github actions pause after driver installation on windows. Apparently it's not always finished at the moment of DSN creation.
1 parent 62010b2 commit 3161979

File tree

16 files changed

+146
-219
lines changed

16 files changed

+146
-219
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ jobs:
124124
#$procLog.Kill()
125125
}
126126
echo "=========="
127+
Start-Sleep -Milliseconds 300
127128
Get-OdbcDriver -Platform "64-bit"
128129
Add-OdbcDsn -Name $env:TEST_DSN -DriverName "MariaDB ODBC 3.2 Driver" -DsnType "User" -SetPropertyValue @( "SERVER=$env:TEST_SERVER", "DATABASE=$env:TEST_SCHEMA", "USER=$env:TEST_UID", "PASSWORD=$env:TEST_PASSWORD", "PORT=$env:TEST_PORT" )
129130
@@ -159,7 +160,7 @@ jobs:
159160
ctest --verbose
160161
161162
export TEST_ADD_PARAM="STREAMRS=1;FORWARDONLY=1"
162-
#ctest --output-on-failure
163+
ctest --output-on-failure
163164
164165
env:
165166
DB_TYPE: ${{ matrix.db-type }}

driver/class/BinRow.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1115,8 +1115,8 @@ namespace mariadb
11151115
rowDataCache.emplace_back();
11161116
}
11171117
else {
1118-
//rowDataCache.emplace_back(bind[i].length_value, static_cast<const char*>(bind[i].buffer));
1119-
rowDataCache.emplace_back(b.length && *b.length > 0 ? *b.length : b.buffer_length);
1118+
// If we could guarantee, that "b.length && *b.length > 0 ? *b.length :" is for current row, we could use it
1119+
rowDataCache.emplace_back(b.buffer_length);
11201120
b.buffer= const_cast<char*>(rowDataCache.back().arr);
11211121
mysql_stmt_fetch_column(stmt, &b, static_cast<unsigned int>(i), 0);
11221122
}

driver/class/Results.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@ namespace mariadb
3232
class Results {
3333

3434
PreparedStatement* statement= nullptr;
35-
ServerPrepareResult* serverPrepResult= nullptr; //?
35+
ServerPrepareResult* serverPrepResult= nullptr;
3636
int32_t fetchSize= 0;
3737
bool batch= false;
3838
std::size_t expectedSize= 1;
3939
Unique::CmdInformation cmdInformation;
4040
std::deque<Unique::ResultSet> executionResults;
4141
Unique::ResultSet currentRs;
4242
ResultSet* resultSet= nullptr;
43-
Unique::ResultSet callableResultSet; //?
43+
Unique::ResultSet callableResultSet;
4444
bool binaryFormat= false;
4545
int32_t resultSetScrollType;
4646
bool rewritten= false;

driver/class/SSPSDirectExec.cpp

Lines changed: 1 addition & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -49,31 +49,7 @@ namespace mariadb
4949
{
5050
}
5151

52-
/* For cloning */
53-
/*SSPSDirectExec::SSPSDirectExec(
54-
Protocol* _connection,
55-
int32_t resultSetScrollType)
56-
: ServerSidePreparedStatement(_connection, resultSetScrollType)
57-
{
58-
}*/
59-
60-
/**
61-
* Clone statement.
62-
*
63-
* @param connection connection
64-
* @return Clone statement.
65-
* @throws CloneNotSupportedException if any error occur.
66-
*/
67-
/*SSPSDirectExec* SSPSDirectExec::clone(Protocol* connection)
68-
{
69-
SSPSDirectExec* clone= new SSPSDirectExec(connection, this->resultSetScrollType);
70-
clone->metadata.reset(new ResultSetMetaData(*metadata));
71-
clone->prepare(*sql);
72-
73-
return clone;
74-
}*/
75-
76-
52+
7753
ResultSetMetaData* SSPSDirectExec::getMetaData()
7854
{
7955
return metadata.release();
@@ -119,25 +95,6 @@ namespace mariadb
11995
}
12096

12197

122-
/*void SSPSDirectExec::executeQueryPrologue(ServerPrepareResult* serverPrepareResult)
123-
{
124-
checkClose();
125-
}*/
126-
127-
128-
/*void SSPSDirectExec::getResult()
129-
{
130-
if (mysql_stmt_field_count(serverPrepareResult->getStatementId()) == 0) {
131-
results->addStats(mysql_stmt_affected_rows(serverPrepareResult->getStatementId()), hasMoreResults());
132-
}
133-
else {
134-
serverPrepareResult->reReadColumnInfo();
135-
ResultSet *rs= ResultSet::create(results.get(), guard, serverPrepareResult);
136-
results->addResultSet(rs, hasMoreResults() || results->getFetchSize() > 0);
137-
}
138-
}*/
139-
140-
14198
bool SSPSDirectExec::executeInternal(int32_t fetchSize)
14299
{
143100
checkClose();
@@ -187,90 +144,4 @@ namespace mariadb
187144
}
188145
}
189146
}
190-
191-
//const char* SSPSDirectExec::getError()
192-
//{
193-
// return mysql_stmt_error(serverPrepareResult->getStatementId());
194-
//}
195-
196-
//uint32_t SSPSDirectExec::getErrno()
197-
//{
198-
// return mysql_stmt_errno(serverPrepareResult->getStatementId());
199-
//}
200-
201-
//const char* SSPSDirectExec::getSqlState()
202-
//{
203-
// return mysql_stmt_sqlstate(serverPrepareResult->getStatementId());
204-
//}
205-
206-
//bool SSPSDirectExec::bind(MYSQL_BIND* param)
207-
//{
208-
// this->param= param;
209-
// return mysql_stmt_bind_param(serverPrepareResult->getStatementId(), param) != '\0';
210-
//}
211-
212-
// bool SSPSDirectExec::sendLongData(uint32_t paramNum, const char* data, std::size_t length)
213-
// {
214-
// return mysql_stmt_send_long_data(serverPrepareResult->getStatementId(), paramNum, data, static_cast<unsigned long>(length)) != '\0';
215-
// }
216-
//
217-
//
218-
// bool SSPSDirectExec::hasMoreResults()
219-
// {
220-
// return results && results->hasMoreResults(guard);//It maybe still safer to call this || mysql_stmt_more_results(serverPrepareResult->getStatementId());
221-
// }
222-
//
223-
//
224-
// void SSPSDirectExec::moveToNextResult()
225-
// {
226-
// guard->moveToNextResult(results.get(), serverPrepareResult);
227-
// }
228-
//
229-
////----------------------------- For param callbacks ------------------------------
230-
//
231-
// bool SSPSDirectExec::setParamCallback(ParamCodec* callback, uint32_t param)
232-
// {
233-
// if (param == uint32_t(-1)) {
234-
// parRowCallback= callback;
235-
// if (callback != nullptr) {
236-
// mysql_stmt_attr_set(serverPrepareResult->getStatementId(), STMT_ATTR_CB_USER_DATA, (void*)this);
237-
// return mysql_stmt_attr_set(serverPrepareResult->getStatementId(), STMT_ATTR_CB_PARAM,
238-
// (const void*)withRowCheckCallback);
239-
// }
240-
// else {
241-
// // Let's say - NULL as row callbback resets everything. That seems to be least ambiguous behavior
242-
// mysql_stmt_attr_set(serverPrepareResult->getStatementId(), STMT_ATTR_CB_USER_DATA, (void*)NULL);
243-
// return mysql_stmt_attr_set(serverPrepareResult->getStatementId(), STMT_ATTR_CB_PARAM,
244-
// (const void*)NULL);
245-
// }
246-
// }
247-
//
248-
// if (param >= serverPrepareResult->getParamCount()) {
249-
// throw SQLException("Invalid parameter number");
250-
// }
251-
//
252-
// parColCodec.insert({param, callback});
253-
// if (parRowCallback == nullptr && parColCodec.size() == 1) {
254-
// // Needed not to overwrite callback with row check
255-
// mysql_stmt_attr_set(serverPrepareResult->getStatementId(), STMT_ATTR_CB_USER_DATA, (void*)this);
256-
// return mysql_stmt_attr_set(serverPrepareResult->getStatementId(), STMT_ATTR_CB_PARAM, (const void*)defaultParamCallback);
257-
// }
258-
// return false;
259-
// }
260-
//
261-
//
262-
// bool SSPSDirectExec::setCallbackData(void * data)
263-
// {
264-
// callbackData= data;
265-
// // if C/C does not support callbacks
266-
// return mysql_stmt_attr_set(serverPrepareResult->getStatementId(), STMT_ATTR_CB_USER_DATA, (void*)this);
267-
// }
268-
//
269-
// /* Checking if next result is the last one. That would mean, that the current is out params.
270-
// * Method does not do any other checks and asusmes they are done by caller - i.e. it's callable result
271-
// */
272-
// bool SSPSDirectExec::isOutParams()
273-
// {
274-
// return results->nextIsLast(guard);
275-
// }
276147
} // namespace mariadb

driver/ma_dll.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
#include "ma_platform_win32.h"
2020
#include <mysql.h>
2121

22+
void DriverGlobalInit(void);
23+
void DriverGlobalClean(void);
24+
2225
BOOL __stdcall DllMain ( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved )
2326
{
2427
switch (fdwReason) {

driver/ma_helper.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -869,14 +869,14 @@ void* GetBindOffset(MADB_Header& Header, void* Ptr, SQLULEN RowNumber, std::size
869869
if (Header.BindType == SQL_BIND_BY_COLUMN ||
870870
Header.BindType == SQL_PARAM_BIND_BY_COLUMN)
871871
{
872-
BindOffset+= PtrSize * RowNumber;
872+
BindOffset+= PtrSize*RowNumber;
873873
}
874874
else
875875
{
876-
BindOffset+= Header.BindType * RowNumber;
876+
BindOffset+= Header.BindType*RowNumber;
877877
}
878878

879-
return (char *)Ptr + BindOffset;
879+
return (char*)Ptr + BindOffset;
880880
}
881881

882882
/* Checking if column ignored in all bound rows. Should hel*/

driver/ma_statement.cpp

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,9 @@ void MADB_CloseCursor(MADB_Stmt *Stmt)
8383
MDBUG_C_PRINT(Stmt->Connection, "Closing resultset", Stmt->stmt.get());
8484
try
8585
{
86-
// TODO: that's not right to mess here with Protocol's lock. Protocol should take care of that
8786
Stmt->rs.reset();
88-
if (Stmt->stmt->hasMoreResults()) {
89-
Stmt->Connection->guard->skipAllResults();
87+
while (Stmt->stmt->getMoreResults()) {
88+
//Stmt->Connection->guard->skipAllResults();
9089
}
9190
}
9291
catch (...)
@@ -1339,7 +1338,6 @@ SQLRETURN MADB_StmtExecute(MADB_Stmt *Stmt, bool ExecDirect)
13391338
Stmt->stmt.reset(ssps);
13401339
}
13411340
catch (int32_t) {
1342-
// going further with csps
13431341
}
13441342
}
13451343

test/bulk.c

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ ODBC_TEST(t_odbc149)
610610
SQLWCHAR w[][8]= {{'x', 'x', 'x', 0}, {'y', 'y', 0}, {'z', 'z', 'z', 'a', 0}}, wBuf[16];
611611

612612
OK_SIMPLE_STMT(Stmt, "DROP TABLE IF EXISTS odbc149");
613-
OK_SIMPLE_STMT(Stmt, "CREATE TABLE odbc149 (id int not null, ts timestamp, c varchar(16), b tinyblob, w varchar(16))");
613+
OK_SIMPLE_STMT(Stmt, "CREATE TABLE odbc149 (id INT NOT NULL, ts TIMESTAMP, c VARCHAR(16), b TINYBLOB, w VARCHAR(16))");
614614

615615
/* This cursor closing is required, otherwise DM(not on Windows) freaks out */
616616
CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));
@@ -619,7 +619,8 @@ ODBC_TEST(t_odbc149)
619619
CHECK_STMT_RC(Stmt, SQLSetStmtAttr(Stmt, SQL_ATTR_PARAMSET_SIZE, (SQLPOINTER)MAODBC_ROWS, 0));
620620

621621
CHECK_STMT_RC(Stmt, SQLBindParameter(Stmt, 1, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, id, 0, NULL));
622-
CHECK_STMT_RC(Stmt, SQLBindParameter(Stmt, 2, SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TIMESTAMP, 0, 0, Val, tsLen[0]/* Garbage as buffer len */, tsLen));
622+
CHECK_STMT_RC(Stmt, SQLBindParameter(Stmt, 2, SQL_PARAM_INPUT, SQL_C_TIMESTAMP, SQL_TIMESTAMP, 0, 0, Val,
623+
tsLen[0]/* Garbage as buffer len */, tsLen));
623624
CHECK_STMT_RC(Stmt, SQLBindParameter(Stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 0, 0, c, sizeof(c[0]), cInd));
624625
CHECK_STMT_RC(Stmt, SQLBindParameter(Stmt, 4, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_BINARY, 0, 0, b, sizeof(b[0]), bLen));
625626
CHECK_STMT_RC(Stmt, SQLBindParameter(Stmt, 5, SQL_PARAM_INPUT, SQL_C_WCHAR, SQL_WCHAR, 0, 0, w, sizeof(w[0]), cInd));

test/catalog1.c

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@ ODBC_TEST(my_columns_null)
3939
(SQLCHAR *)"my_column_null", SQL_NTS,
4040
NULL, SQL_NTS));
4141

42-
CHECK_STMT_RC(Stmt, SQLRowCount(Stmt, &rowCount));
43-
44-
is_num(rowCount, 2);
42+
if (!RSSTREAMING)
43+
{
44+
CHECK_STMT_RC(Stmt, SQLRowCount(Stmt, &rowCount));
45+
is_num(rowCount, 2);
46+
}
4547

4648
is_num(2, my_print_non_format_result(Stmt));
4749

48-
CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));
49-
5050
OK_SIMPLE_STMT(Stmt, "DROP TABLE IF EXISTS my_column_null");
5151

5252
return OK;
@@ -107,8 +107,11 @@ ODBC_TEST(my_table_dbs)
107107
/* Added calls to SQLRowCount just to have tests of it with SQLTables. */
108108
CHECK_STMT_RC(Stmt, SQLRowCount(Stmt, &rowCount));
109109
nrows = my_print_non_format_result(Stmt);
110-
111-
is_num(rowCount, nrows);
110+
// With RS streaming SQLRowCount can't return number of rows in the RS
111+
if (!RSSTREAMING)
112+
{
113+
is_num(rowCount, nrows);
114+
}
112115
CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));
113116

114117
CHECK_STMT_RC(Stmt, SQLTables(Stmt,(SQLCHAR *)SQL_ALL_CATALOGS, SQL_NTS, "", 0, "", 0,
@@ -117,12 +120,13 @@ ODBC_TEST(my_table_dbs)
117120
is_num(nrows, my_print_non_format_result(Stmt));
118121
CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));
119122

120-
CHECK_STMT_RC(Stmt, SQLTables(Stmt,(SQLCHAR *)my_schema, (SQLSMALLINT)strlen(my_schema), NULL, 0, NULL, 0, NULL, 0));
121-
122-
CHECK_STMT_RC(Stmt, SQLRowCount(Stmt, &rowCount));
123-
is_num(rowCount, my_print_non_format_result(Stmt));
124-
125-
CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));
123+
if (!RSSTREAMING)
124+
{
125+
CHECK_STMT_RC(Stmt, SQLTables(Stmt, (SQLCHAR *)my_schema, (SQLSMALLINT)strlen(my_schema), NULL, 0, NULL, 0, NULL, 0));
126+
CHECK_STMT_RC(Stmt, SQLRowCount(Stmt, &rowCount));
127+
is_num(rowCount, my_print_non_format_result(Stmt));
128+
CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));
129+
}
126130

127131
/* test fails on Win2003 x86 w/DM if len=5, SQL_NTS is used instead */
128132
CHECK_STMT_RC(Stmt, SQLTables(Stmt,(SQLCHAR *)"mysql", SQL_NTS, NULL, 0, NULL, 0, NULL, 0));
@@ -131,7 +135,6 @@ ODBC_TEST(my_table_dbs)
131135
CHECK_STMT_RC(Stmt, SQLFreeStmt(Stmt, SQL_CLOSE));
132136

133137
CHECK_STMT_RC(Stmt, SQLTables(Stmt, (SQLCHAR *)"%", 1, "", 0, "", 0, NULL, 0));
134-
135138
CHECK_STMT_RC(Stmt, SQLFetch(Stmt));
136139

137140
memset(database,0,sizeof(database));
@@ -421,10 +424,8 @@ ODBC_TEST(tmysql_specialcols)
421424
OK_SIMPLE_STMT(Stmt,"CREATE TABLE tmysql_specialcols(col1 int primary key, col2 varchar(30), col3 int)");
422425

423426

424-
OK_SIMPLE_STMT(Stmt,"create index tmysql_ind1 on tmysql_specialcols(col1)");
425-
427+
OK_SIMPLE_STMT(Stmt,"CREATE INDEX tmysql_ind1 ON tmysql_specialcols(col1)");
426428
OK_SIMPLE_STMT(Stmt,"INSERT INTO tmysql_specialcols VALUES(100,'venu',1)");
427-
428429
OK_SIMPLE_STMT(Stmt,"INSERT INTO tmysql_specialcols VALUES(200,'MySQL',2)");
429430

430431
CHECK_DBC_RC(Connection, SQLTransact(NULL,Connection,SQL_COMMIT));
@@ -445,7 +446,7 @@ ODBC_TEST(tmysql_specialcols)
445446

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

448-
OK_SIMPLE_STMT(Stmt,"drop table tmysql_specialcols");
449+
OK_SIMPLE_STMT(Stmt,"DROP TABLE tmysql_specialcols");
449450

450451
CHECK_DBC_RC(Connection, SQLTransact(NULL,Connection,SQL_COMMIT));
451452

0 commit comments

Comments
 (0)