Skip to content

Commit 6ef966f

Browse files
committed
ODBC-476 Driver should not use NULL values for (parts of) unique key
The fix and the testcase. Also driver should not stop on error on one of rows and continue to others, returning aggregated result. That is for SQL_DELETE in particular. Re-organized code a bit in oreder not to duplicate checks and common actions for different operations in SQLSetPos
1 parent 7c02e9b commit 6ef966f

File tree

7 files changed

+140
-118
lines changed

7 files changed

+140
-118
lines changed

driver/ma_helper.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ int MADB_KeyTypeCount(MADB_Dbc *Connection, char *TableName, int *PrimaryKeysCou
8585
MYSQL_RES *Res;
8686
MYSQL_FIELD *Field;
8787

88+
// It shouldn't be current but the one from the query
8889
Connection->GetAttr(SQL_ATTR_CURRENT_CATALOG, Database, sizeof(Database), NULL, FALSE);
8990
p+= _snprintf(p, sizeof(StmtStr), "SELECT * FROM ");
9091
if (Database[0] != '\0')
@@ -1173,7 +1174,7 @@ SQLRETURN MADB_DaeStmt(MADB_Stmt *Stmt, SQLUSMALLINT Operation)
11731174
break;
11741175
case SQL_UPDATE:
11751176
Query.assign("UPDATE `").append(CatalogName).append("`.`").append(TableName).append(1, '`');
1176-
if (MADB_DynStrUpdateSet(Stmt, Query)|| MADB_DynStrGetWhere(Stmt, Query, TableName, false))
1177+
if (MADB_DynStrUpdateSet(Stmt, Query) || MADB_DynStrGetWhere(Stmt, Query, TableName, false))
11771178
{
11781179
return Stmt->Error.ReturnValue;
11791180
}

driver/ma_odbc.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -340,6 +340,9 @@ struct MADB_Stmt
340340
bool PositionedCommand= false;
341341
bool RebindParams= false;
342342
bool bind_done= false;
343+
char IndexType= 0; /* For positional operation - the type of index in the UniqueIndex(value of the
344+
unique or pri flag) or > 0 if all columns in the table(UniqueIndex==NULL).
345+
0 - no info, <0 - can't build index */
343346

344347
MADB_Stmt(MADB_Dbc *Connection);
345348
SQLRETURN Prepare(const char* StatementText, SQLINTEGER TextLength, bool ServerSide= true, bool DirectExecution= false);

driver/ma_statement.cpp

Lines changed: 63 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -325,6 +325,7 @@ SQLRETURN MADB_StmtReset(MADB_Stmt* Stmt)
325325
Stmt->State= MADB_SS_INITED;
326326
MADB_CLEAR_ERROR(&Stmt->Error);
327327
MADB_FREE(Stmt->UniqueIndex);
328+
Stmt->IndexType= 0;
328329
MADB_FREE(Stmt->TableName);
329330
}
330331
return SQL_SUCCESS;
@@ -734,8 +735,7 @@ SQLRETURN MADB_StmtPutData(MADB_Stmt *Stmt, SQLPOINTER DataPtr, SQLLEN StrLen_or
734735

735736
if (DataPtr != nullptr && StrLen_or_Ind < 0 && StrLen_or_Ind != SQL_NTS && StrLen_or_Ind != SQL_NULL_DATA)
736737
{
737-
MADB_SetError(&Stmt->Error, MADB_ERR_HY090, nullptr, 0);
738-
return Stmt->Error.ReturnValue;
738+
return MADB_SetError(&Stmt->Error, MADB_ERR_HY090, nullptr, 0);
739739
}
740740

741741
if (Stmt->DataExecutionType != MADB_DAE_NORMAL)
@@ -750,8 +750,7 @@ SQLRETURN MADB_StmtPutData(MADB_Stmt *Stmt, SQLPOINTER DataPtr, SQLLEN StrLen_or
750750
/* Check if we've already sent any data */
751751
if (false)// we should have some own flags for that, other than MyStmt->stmt->params[Stmt->PutParam].long_data_used)
752752
{
753-
MADB_SetError(&Stmt->Error, MADB_ERR_HY011, nullptr, 0);
754-
return Stmt->Error.ReturnValue;
753+
return MADB_SetError(&Stmt->Error, MADB_ERR_HY011, nullptr, 0);
755754
}
756755
Record->Type= SQL_TYPE_NULL;
757756
return SQL_SUCCESS;
@@ -760,8 +759,7 @@ SQLRETURN MADB_StmtPutData(MADB_Stmt *Stmt, SQLPOINTER DataPtr, SQLLEN StrLen_or
760759
/* This normally should be enforced by DM */
761760
if (DataPtr == nullptr && StrLen_or_Ind != 0)
762761
{
763-
MADB_SetError(&Stmt->Error, MADB_ERR_HY009, nullptr, 0);
764-
return Stmt->Error.ReturnValue;
762+
return MADB_SetError(&Stmt->Error, MADB_ERR_HY009, nullptr, 0);
765763
}
766764
/*
767765
if (StrLen_or_Ind == SQL_NTS)
@@ -824,11 +822,9 @@ SQLRETURN MADB_ExecutePositionedUpdate(MADB_Stmt *Stmt, bool ExecDirect)
824822
MADB_CLEAR_ERROR(&Stmt->Error);
825823
if (!Stmt->PositionedCursor->result)
826824
{
827-
MADB_SetError(&Stmt->Error, MADB_ERR_34000, "Cursor has no result set or is not open", 0);
828-
return Stmt->Error.ReturnValue;
825+
return MADB_SetError(&Stmt->Error, MADB_ERR_34000, "Cursor has no result set or is not open", 0);
829826
}
830827
MADB_StmtDataSeek(Stmt->PositionedCursor, Stmt->PositionedCursor->Cursor.Position);
831-
Stmt->Methods->RefreshRowPtrs(Stmt->PositionedCursor);
832828

833829
memcpy(&Stmt->Apd->Header, &Stmt->Ard->Header, sizeof(MADB_Header));
834830

@@ -3555,12 +3551,6 @@ SQLRETURN MADB_GetCursorName(MADB_Stmt *Stmt, void *CursorName, SQLSMALLINT Buff
35553551
}
35563552
/* }}} */
35573553

3558-
/* {{{ MADB_RefreshRowPtrs */
3559-
SQLRETURN MADB_RefreshRowPtrs(MADB_Stmt *Stmt)
3560-
{
3561-
return SQL_SUCCESS;// MoveNext(Stmt, 1LL);
3562-
}
3563-
35643554
/* {{{ MADB_RefreshDynamicCursor */
35653555
SQLRETURN MADB_RefreshDynamicCursor(MADB_Stmt *Stmt)
35663556
{
@@ -3602,7 +3592,7 @@ SQLRETURN MADB_StmtSetPos(MADB_Stmt* Stmt, SQLSETPOSIROW RowNumber, SQLUSMALLINT
36023592
return MADB_SetError(&Stmt->Error, MADB_ERR_24000, nullptr, 0);
36033593
}
36043594

3605-
/* This RowNumber != 1 is based on current SQL_POSITION implementation, and actually does not look to be quite correct
3595+
/* This RowNumber != 1 - we don't support block cursors with RS streaming. But with forward only we actually could
36063596
*/
36073597
if (Stmt->Options.CursorType == SQL_CURSOR_FORWARD_ONLY && Operation == SQL_POSITION && RowNumber != 1)
36083598
{
@@ -3613,34 +3603,44 @@ SQLRETURN MADB_StmtSetPos(MADB_Stmt* Stmt, SQLSETPOSIROW RowNumber, SQLUSMALLINT
36133603
{
36143604
return MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, nullptr, 0);
36153605
}
3606+
// SQL_ADD does not care about RowNumber. But probably the error still should be if it is not 0?
3607+
if (Operation != SQL_ADD && (RowNumber > Stmt->rs->rowsCount() || (RowNumber == 0 && Operation == SQL_POSITION)))
3608+
{
3609+
return MADB_SetError(&Stmt->Error, MADB_ERR_HY109, nullptr, 0);
3610+
}
3611+
// I wonder if for SQL_ALL that is even correct. Application kinda supposed to change values to insert them. If we arefresh data
3612+
// from server, we are overwriting those changes. But maybe should just use correct cursor type or SQLBulkOPerations
3613+
if (Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC && !SQL_SUCCEEDED(Stmt->Methods->RefreshDynamicCursor(Stmt)))
3614+
{
3615+
return Stmt->Error.ReturnValue;
3616+
}
36163617

3617-
switch(Operation) {
3618+
switch (Operation) {
36183619
case SQL_POSITION:
36193620
{
3620-
if (RowNumber < 1 || RowNumber > Stmt->rs->rowsCount())
3621-
{
3622-
MADB_SetError(&Stmt->Error, MADB_ERR_HY109, nullptr, 0);
3623-
return Stmt->Error.ReturnValue;
3624-
}
3625-
if (Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC)
3626-
if (!SQL_SUCCEEDED(Stmt->Methods->RefreshDynamicCursor(Stmt)))
3627-
return Stmt->Error.ReturnValue;
3628-
Stmt->Cursor.Position+= (RowNumber - 1);
3621+
Stmt->Cursor.Position += (RowNumber - 1);
36293622
MADB_StmtDataSeek(Stmt, Stmt->Cursor.Position);
36303623
}
3631-
break;
3624+
case SQL_REFRESH:
3625+
/* todo */
3626+
/* Nothing else to do (at least for now) in this 2 cases */
3627+
return SQL_SUCCESS;
3628+
};
3629+
3630+
char* TableName= MADB_GetTableName(Stmt);
3631+
if (!TableName)
3632+
{
3633+
return MADB_SetError(&Stmt->Error, MADB_ERR_IM001, "Updatable Cursors with multiple tables are not supported", 0);
3634+
}
3635+
char* CatalogName= MADB_GetCatalogName(Stmt);
3636+
3637+
switch (Operation) {
36323638
case SQL_ADD:
36333639
{
36343640
MADB_DynString DynStmt;
36353641
SQLRETURN ret;
3636-
char *TableName= MADB_GetTableName(Stmt);
3637-
char *CatalogName= MADB_GetCatalogName(Stmt);
36383642
int column, param= 0;
36393643

3640-
if (Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC)
3641-
if (!SQL_SUCCEEDED(Stmt->Methods->RefreshDynamicCursor(Stmt)))
3642-
return Stmt->Error.ReturnValue;
3643-
36443644
Stmt->DaeRowNumber= RowNumber;
36453645

36463646
if (Stmt->DataExecutionType != MADB_DAE_ADD)
@@ -3721,35 +3721,17 @@ SQLRETURN MADB_StmtSetPos(MADB_Stmt* Stmt, SQLSETPOSIROW RowNumber, SQLUSMALLINT
37213721
break;
37223722
case SQL_UPDATE:
37233723
{
3724-
char *TableName= MADB_GetTableName(Stmt);
37253724
my_ulonglong Start= 0,
37263725
End= Stmt->rs->rowsCount();
3727-
SQLRETURN result= SQL_INVALID_HANDLE; /* Just smth we cannot normally get */
3728-
3729-
if (!TableName)
3730-
{
3731-
MADB_SetError(&Stmt->Error, MADB_ERR_IM001, "Updatable Cursors with multiple tables are not supported", 0);
3732-
return Stmt->Error.ReturnValue;
3733-
}
3726+
SQLRETURN result= SQL_INVALID_HANDLE; /* Just smth we cannot normally get */
37343727

37353728
Stmt->AffectedRows= 0;
37363729

37373730
if ((SQLLEN)RowNumber > Stmt->LastRowFetched)
37383731
{
3739-
MADB_SetError(&Stmt->Error, MADB_ERR_S1107, nullptr, 0);
3740-
return Stmt->Error.ReturnValue;
3732+
return MADB_SetError(&Stmt->Error, MADB_ERR_S1107, nullptr, 0);
37413733
}
37423734

3743-
if (RowNumber < 0 || RowNumber > End)
3744-
{
3745-
MADB_SetError(&Stmt->Error, MADB_ERR_HY109, nullptr, 0);
3746-
return Stmt->Error.ReturnValue;
3747-
}
3748-
3749-
if (Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC)
3750-
if (!SQL_SUCCEEDED(Stmt->Methods->RefreshDynamicCursor(Stmt)))
3751-
return Stmt->Error.ReturnValue;
3752-
37533735
Stmt->DaeRowNumber= MAX(1,RowNumber);
37543736

37553737
/* Cursor is open, but no row was fetched, so we simulate
@@ -3758,11 +3740,12 @@ SQLRETURN MADB_StmtSetPos(MADB_Stmt* Stmt, SQLSETPOSIROW RowNumber, SQLUSMALLINT
37583740
Stmt->Cursor.Position= 1;
37593741

37603742
if (RowNumber)
3761-
Start= End= Stmt->Cursor.Position + RowNumber - 1;
3743+
{
3744+
Start= End = Stmt->Cursor.Position + RowNumber - 1;
3745+
}
37623746
else
37633747
{
37643748
Start= Stmt->Cursor.Position;
3765-
/* TODO: if num_rows returns 1, End is 0? Start would be 1, no */
37663749
End= MIN(Stmt->rs->rowsCount(), Start + Stmt->Ard->Header.ArraySize - 1);
37673750
}
37683751
/* Stmt->ArrayOffset will be incremented in StmtExecute() */
@@ -3773,7 +3756,6 @@ SQLRETURN MADB_StmtSetPos(MADB_Stmt* Stmt, SQLSETPOSIROW RowNumber, SQLUSMALLINT
37733756
{
37743757
SQLSMALLINT param= 0, column;
37753758
MADB_StmtDataSeek(Stmt, Start);
3776-
Stmt->Methods->RefreshRowPtrs(Stmt);
37773759

37783760
/* We don't need to prepare the statement, if SetPos was called
37793761
from SQLParamData() function */
@@ -3839,9 +3821,11 @@ SQLRETURN MADB_StmtSetPos(MADB_Stmt* Stmt, SQLSETPOSIROW RowNumber, SQLUSMALLINT
38393821
++param;
38403822
} /* End of for(column=0;...) */
38413823
if (Stmt->PutParam == MADB_NEED_DATA)
3824+
{
38423825
return SQL_NEED_DATA;
3843-
} /* End of if (!ArrayOffset) */
3844-
3826+
}
3827+
} /* End of if (!ArrayOffset) */
3828+
38453829
if (Stmt->DaeStmt->Methods->Execute(Stmt->DaeStmt, FALSE) != SQL_ERROR)
38463830
{
38473831
Stmt->AffectedRows+= Stmt->DaeStmt->AffectedRows;
@@ -3865,29 +3849,15 @@ SQLRETURN MADB_StmtSetPos(MADB_Stmt* Stmt, SQLSETPOSIROW RowNumber, SQLUSMALLINT
38653849
}
38663850
case SQL_DELETE:
38673851
{
3852+
SQLRETURN result = SQL_INVALID_HANDLE; /* Just smth we cannot normally get */
38683853
SQLString DynamicStmt("DELETE FROM ");
38693854
std::size_t baseStmtLen;
38703855
SQLULEN SaveArraySize= Stmt->Ard->Header.ArraySize;
38713856
my_ulonglong Start= 0,
38723857
End= Stmt->rs->rowsCount();
3873-
char *TableName= MADB_GetTableName(Stmt);
3874-
3875-
if (!TableName)
3876-
{
3877-
MADB_SetError(&Stmt->Error, MADB_ERR_IM001, "Updatable Cursors with multiple tables are not supported", 0);
3878-
return Stmt->Error.ReturnValue;
3879-
}
38803858

38813859
Stmt->Ard->Header.ArraySize= 1;
3882-
if (Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC)
3883-
if (!SQL_SUCCEEDED(Stmt->Methods->RefreshDynamicCursor(Stmt)))
3884-
return Stmt->Error.ReturnValue;
38853860
Stmt->AffectedRows= 0;
3886-
if (RowNumber < 0 || RowNumber > End)
3887-
{
3888-
MADB_SetError(&Stmt->Error, MADB_ERR_HY109, nullptr, 0);
3889-
return Stmt->Error.ReturnValue;
3890-
}
38913861
Start= (RowNumber) ? Stmt->Cursor.Position + RowNumber - 1 : Stmt->Cursor.Position;
38923862
if (SaveArraySize && !RowNumber)
38933863
{
@@ -3898,39 +3868,44 @@ SQLRETURN MADB_StmtSetPos(MADB_Stmt* Stmt, SQLSETPOSIROW RowNumber, SQLUSMALLINT
38983868
End= Start;
38993869
}
39003870
DynamicStmt.reserve(8182);
3871+
DynamicStmt.append(TableName);
39013872
baseStmtLen= DynamicStmt.length();
39023873
while (Start <= End)
39033874
{
39043875
MADB_StmtDataSeek(Stmt, Start);
3905-
Stmt->Methods->RefreshRowPtrs(Stmt);
3906-
DynamicStmt.append(TableName);
3907-
3876+
39083877
if (MADB_DynStrGetWhere(Stmt, DynamicStmt, TableName, false))
39093878
{
3910-
return Stmt->Error.ReturnValue;
3879+
MADB_SETPOS_AGG_RESULT(result, Stmt->Error.ReturnValue);
3880+
/* Continuing to next row. If there is no key - we will get it for all rows and as aggregated result.
3881+
* if we have unique key and NULL value on part of the key - we need to continue to next row (we wouldn't
3882+
* stop here before when we did not care about NULL values)
3883+
*/
3884+
}
3885+
else
3886+
{
3887+
std::lock_guard<std::mutex> localScopeLock(Stmt->Connection->guard->getLock());
3888+
Stmt->Connection->guard->safeRealQuery(DynamicStmt);
3889+
Stmt->AffectedRows += mysql_affected_rows(Stmt->Connection->mariadb);
39113890
}
3912-
3913-
std::lock_guard<std::mutex> localScopeLock(Stmt->Connection->guard->getLock());
3914-
Stmt->Connection->guard->safeRealQuery(DynamicStmt);
3915-
3916-
Stmt->AffectedRows+= mysql_affected_rows(Stmt->Connection->mariadb);
39173891
++Start;
39183892
DynamicStmt.erase(baseStmtLen);
3893+
/* Technichally we could combine here all conditions with OR. Other oportunity would be to use
3894+
* bulk operation, but OR is like way easier to make. But we won't know results for individual rows.
3895+
*/
39193896
}
39203897
Stmt->Ard->Header.ArraySize= SaveArraySize;
39213898
/* if we have a dynamic cursor we need to adjust the rowset size */
39223899
if (Stmt->Options.CursorType == SQL_CURSOR_DYNAMIC)
39233900
{
39243901
Stmt->LastRowFetched-= (unsigned long)Stmt->AffectedRows;
39253902
}
3903+
/* Making sure we do not return initial value */
3904+
return result == SQL_INVALID_HANDLE ? SQL_SUCCESS : result;
39263905
}
39273906
break;
3928-
case SQL_REFRESH:
3929-
/* todo*/
3930-
break;
39313907
default:
3932-
MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, "Only SQL_POSITION and SQL_REFRESH Operations are supported", 0);
3933-
return Stmt->Error.ReturnValue;
3908+
return MADB_SetError(&Stmt->Error, MADB_ERR_HYC00, "Only SQL_POSITION and SQL_REFRESH Operations are supported", 0);
39343909
}
39353910
return SQL_SUCCESS;
39363911
}
@@ -4113,8 +4088,7 @@ struct st_ma_stmt_methods MADB_StmtMethods=
41134088
MADB_StmtParamData,
41144089
MADB_StmtPutData,
41154090
MADB_StmtBulkOperations,
4116-
MADB_RefreshDynamicCursor,
4117-
MADB_RefreshRowPtrs
4091+
MADB_RefreshDynamicCursor
41184092
};
41194093

41204094
MADB_Stmt::MADB_Stmt(MADB_Dbc* Dbc)

driver/ma_statement.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,6 @@ struct st_ma_stmt_methods
9595
SQLRETURN(*PutData)(MADB_Stmt* Stmt, SQLPOINTER DataPtr, SQLLEN StrLen_or_Ind);
9696
SQLRETURN(*BulkOperations)(MADB_Stmt* Stmt, SQLSMALLINT Operation);
9797
SQLRETURN(*RefreshDynamicCursor)(MADB_Stmt* Stmt);
98-
SQLRETURN(*RefreshRowPtrs)(MADB_Stmt* Stmt);
9998
};
10099

101100
SQLRETURN MADB_StmtInit (MADB_Dbc *Connection, SQLHANDLE *pHStmt, bool external= true);

0 commit comments

Comments
 (0)