From: Piotr Caban <piotr(a)codeweavers.com> --- dlls/msado15/recordset.c | 149 ++++++++++++++++++++++++++++++----- dlls/msado15/tests/msado15.c | 116 +++++++++++++++++++++------ 2 files changed, 222 insertions(+), 43 deletions(-) diff --git a/dlls/msado15/recordset.c b/dlls/msado15/recordset.c index a46a16e1251..4aa72c039d0 100644 --- a/dlls/msado15/recordset.c +++ b/dlls/msado15/recordset.c @@ -51,6 +51,8 @@ struct field unsigned char prec; unsigned char scale; struct recordset *recordset; + HACCESSOR hacc_get; + HACCESSOR hacc_put; /* Field Properties */ VARIANT optimize; @@ -385,46 +387,139 @@ static LONG get_column_count( struct recordset *recordset ) static HRESULT WINAPI field_get_Value( Field *iface, VARIANT *val ) { struct field *field = impl_from_Field( iface ); - ULONG row = field->recordset->index, col = field->index, col_count; - VARIANT copy; + struct recordset *recordset = field->recordset; + struct buf + { + VARIANT val; + DBSTATUS status; + } buf; HRESULT hr; TRACE( "%p, %p\n", field, val ); - if (field->recordset->state == adStateClosed) return MAKE_ADO_HRESULT( adErrObjectClosed ); - if (field->recordset->index < 0) return MAKE_ADO_HRESULT( adErrNoCurrentRecord ); + if (!recordset || recordset->state == adStateClosed) return MAKE_ADO_HRESULT( adErrObjectClosed ); + + if (!recordset->is_eof && !recordset->is_bof && !recordset->current_row) + { + hr = cache_get( recordset, TRUE ); + if (FAILED(hr)) return hr; + } + if (!recordset->current_row) return MAKE_ADO_HRESULT( adErrNoCurrentRecord ); + + if (!recordset->accessor) + { + hr = IRowset_QueryInterface( recordset->row_set, &IID_IAccessor, (void **)&recordset->accessor ); + if (FAILED(hr) || !recordset->accessor) + recordset->accessor = NO_INTERFACE; + } + if (recordset->accessor == NO_INTERFACE) + return MAKE_ADO_HRESULT( adErrFeatureNotAvailable ); + + if (!field->hacc_get) + { + DBBINDSTATUS status = DBBINDSTATUS_OK; + DBBINDING binding; - col_count = get_column_count( field->recordset ); + memset(&binding, 0, sizeof(binding)); + binding.iOrdinal = field->ordinal; + binding.obStatus = offsetof(struct buf, status); + binding.dwPart = DBPART_VALUE | DBPART_STATUS; + binding.cbMaxLen = sizeof(buf.val); + binding.wType = DBTYPE_VARIANT; + binding.bPrecision = field->prec; + binding.bScale = field->scale; + hr = IAccessor_CreateAccessor( recordset->accessor, DBACCESSOR_ROWDATA, + 1, &binding, 0, &field->hacc_get, &status ); + if (FAILED(hr)) return hr; + if (status != DBBINDSTATUS_OK) + { + IAccessor_ReleaseAccessor( recordset->accessor, field->hacc_get, NULL ); + field->hacc_get = 0; + return E_FAIL; + } + } - VariantInit( © ); - if ((hr = VariantCopy( ©, &field->recordset->data[row * col_count + col] )) != S_OK) return hr; + memset(&buf, 0, sizeof(buf)); + hr = IRowset_GetData(recordset->row_set, recordset->current_row, field->hacc_get, &buf); + if (FAILED(hr)) return hr; + if (buf.status != DBSTATUS_S_OK) return E_FAIL; - *val = copy; + *val = buf.val; return S_OK; } static HRESULT WINAPI field_put_Value( Field *iface, VARIANT val ) { struct field *field = impl_from_Field( iface ); - ULONG row = field->recordset->index, col = field->index, col_count; - VARIANT copy; + struct recordset *recordset = field->recordset; + struct buf + { + VARIANT val; + DBSTATUS status; + DBLENGTH len; + } buf; HRESULT hr; TRACE( "%p, %s\n", field, debugstr_variant(&val) ); - if (field->recordset->state == adStateClosed) return MAKE_ADO_HRESULT( adErrObjectClosed ); - if (field->recordset->index < 0) return MAKE_ADO_HRESULT( adErrNoCurrentRecord ); + if (!recordset || recordset->state == adStateClosed) return MAKE_ADO_HRESULT( adErrObjectClosed ); - col_count = get_column_count( field->recordset ); + if (!recordset->is_eof && !recordset->is_bof && !recordset->current_row) + { + hr = cache_get( recordset, TRUE ); + if (FAILED(hr)) return hr; + } + if (!recordset->current_row) return MAKE_ADO_HRESULT( adErrNoCurrentRecord ); - VariantInit( © ); - if ((hr = VariantCopy( ©, &val )) != S_OK) return hr; + if (!recordset->rowset_change) + { + hr = IRowset_QueryInterface( recordset->row_set, &IID_IRowsetChange, + (void **)&recordset->rowset_change ); + if (FAILED(hr) || !recordset->rowset_change) + recordset->rowset_change = NO_INTERFACE; + } + if (recordset->rowset_change == NO_INTERFACE) + return MAKE_ADO_HRESULT( adErrFeatureNotAvailable ); - field->recordset->data[row * col_count + col] = copy; + if (!recordset->accessor) + { + hr = IRowset_QueryInterface( recordset->row_set, &IID_IAccessor, (void **)&recordset->accessor ); + if (FAILED(hr) || !recordset->accessor) + recordset->accessor = NO_INTERFACE; + } + if (recordset->accessor == NO_INTERFACE) + return MAKE_ADO_HRESULT( adErrFeatureNotAvailable ); - if (field->recordset->editmode == adEditNone) - field->recordset->editmode = adEditInProgress; + if (!field->hacc_put) + { + DBBINDSTATUS status = DBBINDSTATUS_OK; + DBBINDING binding; + memset(&binding, 0, sizeof(binding)); + binding.iOrdinal = field->ordinal; + binding.obLength = offsetof(struct buf, len); + binding.obStatus = offsetof(struct buf, status); + binding.dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS; + binding.cbMaxLen = sizeof(buf.val); + binding.wType = DBTYPE_VARIANT; + binding.bPrecision = field->prec; + binding.bScale = field->scale; + hr = IAccessor_CreateAccessor( recordset->accessor, DBACCESSOR_ROWDATA, + 1, &binding, 0, &field->hacc_put, &status ); + if (FAILED(hr)) return hr; + if (status != DBBINDSTATUS_OK) + { + IAccessor_ReleaseAccessor( recordset->accessor, field->hacc_put, NULL ); + field->hacc_put = 0; + return E_FAIL; + } + } + + memset(&buf, 0, sizeof(buf)); + buf.val = val; + hr = IRowsetChange_SetData(recordset->rowset_change, recordset->current_row, field->hacc_put, &buf); + if (FAILED(hr)) return hr; + if (buf.status != DBSTATUS_S_OK) return E_FAIL; return S_OK; } @@ -1420,9 +1515,6 @@ static void close_recordset( struct recordset *recordset ) if ( recordset->rowset_change && recordset->rowset_change != NO_INTERFACE ) IRowsetChange_Release( recordset->rowset_change ); recordset->rowset_change = NULL; - if (recordset->accessor && recordset->accessor != NO_INTERFACE ) - IAccessor_Release( recordset->accessor ); - recordset->accessor = NULL; VariantClear( &recordset->filter ); @@ -1432,13 +1524,28 @@ static void close_recordset( struct recordset *recordset ) for (i = 0; i < col_count; i++) { + if (recordset->fields.field[i]->hacc_get) + { + IAccessor_ReleaseAccessor(recordset->accessor, recordset->fields.field[i]->hacc_get, NULL); + recordset->fields.field[i]->hacc_get = 0; + } + if (recordset->fields.field[i]->hacc_put) + { + IAccessor_ReleaseAccessor(recordset->accessor, recordset->fields.field[i]->hacc_put, NULL); + recordset->fields.field[i]->hacc_put = 0; + } recordset->fields.field[i]->recordset = NULL; + Field_Release(&recordset->fields.field[i]->Field_iface); if (recordset->haccessors) IAccessor_ReleaseAccessor(accessor, recordset->haccessors[i], NULL); } + if (recordset->accessor && recordset->accessor != NO_INTERFACE ) + IAccessor_Release( recordset->accessor ); + recordset->accessor = NULL; + if (recordset->haccessors) { IAccessor_Release(accessor); diff --git a/dlls/msado15/tests/msado15.c b/dlls/msado15/tests/msado15.c index a73ac67ec6d..3cede5c170c 100644 --- a/dlls/msado15/tests/msado15.c +++ b/dlls/msado15/tests/msado15.c @@ -27,6 +27,7 @@ #include "wine/test.h" #include "msdasql.h" #include "odbcinst.h" +#include "msdadc.h" #define MAKE_ADO_HRESULT( err ) MAKE_HRESULT( SEVERITY_ERROR, FACILITY_CONTROL, err ) @@ -83,6 +84,7 @@ DEFINE_EXPECT(rowset_ReleaseRows); DEFINE_EXPECT(rowset_GetRowsAt); DEFINE_EXPECT(rowset_GetExactPosition); DEFINE_EXPECT(rowset_GetData); +DEFINE_EXPECT(rowset_change_SetData); DEFINE_EXPECT(rowset_change_InsertRow); DEFINE_EXPECT(accessor_AddRefAccessor); DEFINE_EXPECT(accessor_CreateAccessor); @@ -837,8 +839,18 @@ static HRESULT WINAPI rowset_change_DeleteRows(IRowsetChange *iface, HCHAPTER re static HRESULT WINAPI rowset_change_SetData(IRowsetChange *iface, HROW row, HACCESSOR accessor, void *data) { - ok(0, "unexpected call\n"); - return E_NOTIMPL; + VARIANT *v; + + CHECK_EXPECT(rowset_change_SetData); + + ok(row == 1, "row = %Id\n", row); + ok(accessor, "accessor = 0\n"); + ok(data != NULL, "data = NULL\n"); + + v = (VARIANT *)data; + ok(V_VT(v) == VT_I4, "V_VT(v) = %d\n", V_VT(v)); + ok(V_I4(v) == 123, "V_I4(v) = %ld\n", V_I4(v)); + return S_OK; } static HRESULT WINAPI rowset_change_InsertRow(IRowsetChange *iface, @@ -904,7 +916,6 @@ static HRESULT WINAPI accessor_CreateAccessor(IAccessor *iface, DBACCESSORFLAGS ok(!cBindings || cBindings == 1, "cBindings = %Iu\n", cBindings); ok(cbRowSize == 0, "cbRowSize = %Iu\n", cbRowSize); ok(phAccessor != NULL, "pHAccessor = NULL\n"); - ok(!rgStatus, "rgStatus != NULL\n"); if (!cBindings) { @@ -916,6 +927,8 @@ static HRESULT WINAPI accessor_CreateAccessor(IAccessor *iface, DBACCESSORFLAGS haccessor->ref = 1; haccessor->binding = rgBindings[0]; *phAccessor = (HACCESSOR)haccessor; + + if (rgStatus) rgStatus[0] = DBBINDSTATUS_OK; return S_OK; } @@ -1049,25 +1062,49 @@ static HRESULT WINAPI rowset_AddRefRows(IRowsetExactScroll *iface, DBCOUNTITEM c static HRESULT WINAPI rowset_GetData(IRowsetExactScroll *iface, HROW hRow, HACCESSOR hAccessor, void *pData) { struct haccessor *haccessor = (struct haccessor *)hAccessor; - DBSTATUS status; + DBSTATUS status = DBSTATUS_S_OK; DBLENGTH len; - int val; + union + { + int i; + VARIANT v; + } val; CHECK_EXPECT2(rowset_GetData); - ok(!haccessor->binding.iOrdinal, "iOrdinal = %Id\n", haccessor->binding.iOrdinal); - ok(haccessor->binding.dwPart == (DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS), - "dwPart = %ld\n", haccessor->binding.dwPart); - ok(haccessor->binding.cbMaxLen == sizeof(int), "cbMaxLen = %Id\n", haccessor->binding.cbMaxLen); - ok(haccessor->binding.wType == DBTYPE_I4, "wType = %d\n", haccessor->binding.wType); - - val = hRow; - len = sizeof(int); - status = DBSTATUS_S_OK; + switch(haccessor->binding.iOrdinal) + { + case 0: + ok(haccessor->binding.dwPart == (DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS), + "dwPart = %ld\n", haccessor->binding.dwPart); + ok(haccessor->binding.cbMaxLen == sizeof(int), "cbMaxLen = %Id\n", haccessor->binding.cbMaxLen); + ok(haccessor->binding.wType == DBTYPE_I4, "wType = %d\n", haccessor->binding.wType); + + val.i = hRow; + len = sizeof(val.i); + break; + + case 1: + ok(haccessor->binding.dwPart == (DBPART_VALUE | DBPART_STATUS), + "dwPart = %ld\n", haccessor->binding.dwPart); + ok(haccessor->binding.cbMaxLen == sizeof(VARIANT), "cbMaxLen = %Id\n", haccessor->binding.cbMaxLen); + ok(haccessor->binding.wType == DBTYPE_VARIANT, "wType = %d\n", haccessor->binding.wType); + + V_VT(&val.v) = VT_I4; + V_I4(&val.v) = 123; + len = sizeof(val.v); + break; + default: + ok(0, "unexpected GetData argument\n"); + return E_NOTIMPL; + } - memcpy((BYTE *)pData + haccessor->binding.obValue, &val, sizeof(val)); - memcpy((BYTE *)pData + haccessor->binding.obLength, &len, sizeof(len)); - memcpy((BYTE *)pData + haccessor->binding.obStatus, &status, sizeof(status)); + if (haccessor->binding.dwPart & DBPART_VALUE) + memcpy((BYTE *)pData + haccessor->binding.obValue, &val, len); + if (haccessor->binding.dwPart & DBPART_LENGTH) + memcpy((BYTE *)pData + haccessor->binding.obLength, &len, sizeof(len)); + if (haccessor->binding.dwPart & DBPART_STATUS) + memcpy((BYTE *)pData + haccessor->binding.obStatus, &status, sizeof(status)); return S_OK; } @@ -1235,7 +1272,7 @@ static void test_ADORecordsetConstruction(BOOL exact_scroll) HRESULT hr; LONG count, state; unsigned char prec, scale; - VARIANT index, missing; + VARIANT index, missing, v; ADO_LONGPTR size; DataTypeEnum type; @@ -1334,6 +1371,43 @@ static void test_ADORecordsetConstruction(BOOL exact_scroll) ok( hr == S_OK, "got %08lx\n", hr ); ok( scale == 1, "got %u\n", scale ); + SET_EXPECT( rowset_GetNextRows ); + if (exact_scroll) SET_EXPECT( rowset_GetRowsAt ); + SET_EXPECT( rowset_QI_IAccessor ); + SET_EXPECT( accessor_CreateAccessor ); + SET_EXPECT( rowset_GetData ); + hr = Field_get_Value( field, &v ); + ok( hr == S_OK, "got %08lx\n", hr ); + if (!exact_scroll) CHECK_CALLED( rowset_GetNextRows ); + else + { + todo_wine CHECK_NOT_CALLED( rowset_GetNextRows ); + todo_wine CHECK_CALLED( rowset_GetRowsAt ); + } + CHECK_CALLED( rowset_QI_IAccessor ); + CHECK_CALLED( accessor_CreateAccessor ); + CHECK_CALLED( rowset_GetData ); + ok( V_VT(&v) == VT_I4, "V_VT(&v) = %d\n", V_VT(&v) ); + ok( V_I4(&v) == 123, "V_I4(&v) = %ld\n", V_I4(&v) ); + + SET_EXPECT( rowset_GetData ); + hr = Field_get_Value( field, &v ); + ok( hr == S_OK, "got %08lx\n", hr ); + CHECK_CALLED( rowset_GetData ); + ok( V_VT(&v) == VT_I4, "V_VT(&v) = %d\n", V_VT(&v) ); + ok( V_I4(&v) == 123, "V_I4(&v) = %ld\n", V_I4(&v) ); + + SET_EXPECT(rowset_QI_IRowsetChange); + SET_EXPECT( rowset_QI_IAccessor ); + SET_EXPECT( accessor_CreateAccessor ); + SET_EXPECT( rowset_change_SetData ); + hr = Field_put_Value( field, v ); + ok( hr == S_OK, "got %08lx\n", hr ); + todo_wine CHECK_NOT_CALLED(rowset_QI_IRowsetChange); + todo_wine CHECK_CALLED( rowset_QI_IAccessor ); + CHECK_CALLED( accessor_CreateAccessor ); + CHECK_CALLED( rowset_change_SetData ); + Field_Release( field ); SET_EXPECT( rowset_QI_IRowsetExactScroll ); @@ -1394,7 +1468,6 @@ static void test_ADORecordsetConstruction(BOOL exact_scroll) V_VT( &missing ) = VT_ERROR; V_ERROR( &missing ) = DISP_E_PARAMNOTFOUND; - SET_EXPECT(rowset_QI_IRowsetChange); SET_EXPECT(rowset_QI_IAccessor); SET_EXPECT(accessor_CreateAccessor); SET_EXPECT(accessor_AddRefAccessor); @@ -1403,9 +1476,8 @@ static void test_ADORecordsetConstruction(BOOL exact_scroll) if (exact_scroll) SET_EXPECT(rowset_GetData); hr = _Recordset_AddNew( recordset, missing, missing ); ok( hr == S_OK, "got %08lx\n", hr ); - todo_wine CHECK_NOT_CALLED(rowset_QI_IRowsetChange); - if (!exact_scroll) CHECK_CALLED(rowset_QI_IAccessor); - else todo_wine CHECK_NOT_CALLED(rowset_QI_IAccessor); + if (!exact_scroll) todo_wine CHECK_CALLED(rowset_QI_IAccessor); + else CHECK_NOT_CALLED(rowset_QI_IAccessor); CHECK_CALLED(accessor_CreateAccessor); CHECK_CALLED(accessor_AddRefAccessor); CHECK_CALLED(rowset_change_InsertRow); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9531