[PATCH 0/4] MR9691: msado15: Support setting values in _Recordset AddNew and Update.
From: Piotr Caban <piotr(a)codeweavers.com> --- dlls/msado15/recordset.c | 179 +++++++++++++++++++++++++++++++++------ 1 file changed, 152 insertions(+), 27 deletions(-) diff --git a/dlls/msado15/recordset.c b/dlls/msado15/recordset.c index 611c509003d..1c5c7cbe533 100644 --- a/dlls/msado15/recordset.c +++ b/dlls/msado15/recordset.c @@ -28,6 +28,7 @@ #include "oledberr.h" #include "sqlucode.h" +#include "wine/rbtree.h" #include "wine/debug.h" #include "msado15_private.h" @@ -82,6 +83,17 @@ struct bookmark_data DBSTATUS status; }; +struct hacc_cache_elem +{ + struct rb_entry entry; + HACCESSOR hacc; + struct hacc_cache_key + { + int len; + ULONG data[1]; + } key; +}; + struct recordset { _Recordset Recordset_iface; @@ -117,7 +129,7 @@ struct recordset VARIANT_BOOL is_eof; ADO_LONGPTR max_records; - HACCESSOR hacc_empty; /* haccessor for adding empty rows */ + struct rb_tree hacc_cache; HACCESSOR bookmark_hacc; DBTYPE bookmark_type; @@ -1626,17 +1638,23 @@ static ULONG WINAPI recordset_AddRef( _Recordset *iface ) return refs; } +static void free_hacc( struct rb_entry *entry, void *context ) +{ + struct hacc_cache_elem *elem = (struct hacc_cache_elem *)entry; + struct recordset *recordset = context; + + IAccessor_ReleaseAccessor( recordset->accessor, elem->hacc, NULL ); + free( elem ); +} + static void close_recordset( struct recordset *recordset ) { int i, j; cache_release( recordset ); recordset->is_bof = recordset->is_eof = VARIANT_FALSE; - if ( recordset->hacc_empty ) - { - IAccessor_ReleaseAccessor( recordset->accessor, recordset->hacc_empty, NULL ); - recordset->hacc_empty = 0; - } + + rb_destroy( &recordset->hacc_cache, free_hacc, recordset ); if (recordset->bookmark_hacc) { @@ -2157,10 +2175,123 @@ static HRESULT WINAPI recordset_get_Source( _Recordset *iface, VARIANT *source ) return E_NOTIMPL; } +static HRESULT get_accessor( struct recordset *recordset, VARIANT *fields, HACCESSOR *hacc ) +{ + struct hacc_cache_elem *elem; + struct hacc_cache_key *key; + DBREFCOUNT refcount; + BYTE tmp[256]; + int i, size; + HRESULT hr; + ULONG idx; + + 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 ); + + hr = init_fields( &recordset->fields ); + if (FAILED(hr)) return hr; + + key = (struct hacc_cache_key *)tmp; + if (V_VT(fields) == VT_ERROR && V_ERROR(fields) == DISP_E_PARAMNOTFOUND) + { + key->len = 0; + } + else if (V_VT(fields) & VT_ARRAY) + { + if (V_VT(fields) != (VT_ARRAY | VT_VARIANT)) + return MAKE_ADO_HRESULT( adErrInvalidArgument ); + + i = V_ARRAY(fields)->rgsabound[0].cElements; + size = offsetof( struct hacc_cache_key, data[i] ); + if (size > sizeof(tmp)) + { + key = malloc( size ); + if (!key) return E_OUTOFMEMORY; + } + key->len = i; + + for (i = 0; i < key->len; i++) + { + hr = map_index( &recordset->fields, ((VARIANT *)V_ARRAY(fields)->pvData) + i, &idx ); + if (FAILED(hr)) + { + if ((BYTE *)key != tmp) free( key ); + return hr; + } + + key->data[i] = idx; + } + } + else + { + hr = map_index( &recordset->fields, fields, &idx ); + if (FAILED(hr)) return hr; + + key->len = 1; + key->data[0] = idx; + } + + elem = (struct hacc_cache_elem *)rb_get( &recordset->hacc_cache, key ); + if (!elem) + { + DBBINDING *bindings = malloc( sizeof(*bindings) * key->len ); + + if (!bindings) + { + if ((BYTE *)key != tmp) free( key ); + return E_OUTOFMEMORY; + } + memset( bindings, 0, sizeof(*bindings) * key->len ); + + elem = malloc( offsetof(struct hacc_cache_elem, key.data[key->len]) ); + if (!elem) + { + if ((BYTE *)key != tmp) free( key ); + free( bindings ); + } + + for (i = 0; i < key->len; i++) + { + struct field *field = recordset->fields.field[key->data[i]]; + + bindings[i].iOrdinal = field->ordinal; + bindings[i].obValue = i * sizeof(VARIANT); + bindings[i].dwPart = DBPART_VALUE; + bindings[i].cbMaxLen = sizeof(VARIANT); + bindings[i].wType = DBTYPE_VARIANT; + bindings[i].bPrecision = field->prec; + bindings[i].bScale = field->scale; + } + hr = IAccessor_CreateAccessor( recordset->accessor, DBACCESSOR_ROWDATA, + key->len, bindings, 0, &elem->hacc, NULL ); + free( bindings ); + if (SUCCEEDED( hr )) + { + elem->key.len = key->len; + memcpy( elem->key.data, key->data, key->len * sizeof(key->data[0]) ); + rb_put( &recordset->hacc_cache, key, &elem->entry ); + } + free( key ); + } + if ((BYTE *)key != tmp) free( key ); + + hr = IAccessor_AddRefAccessor( recordset->accessor, elem->hacc, &refcount ); + if (FAILED(hr)) return hr; + *hacc = elem->hacc; + return S_OK; +} + static HRESULT WINAPI recordset_AddNew( _Recordset *iface, VARIANT field_list, VARIANT values ) { struct recordset *recordset = impl_from_Recordset( iface ); DBREFCOUNT refcount; + HACCESSOR hacc; HRESULT hr; TRACE( "%p, %s, %s\n", recordset, debugstr_variant(&field_list), debugstr_variant(&values) ); @@ -2179,33 +2310,16 @@ static HRESULT WINAPI recordset_AddNew( _Recordset *iface, VARIANT field_list, V if (recordset->rowset_change == NO_INTERFACE) return MAKE_ADO_HRESULT( adErrFeatureNotAvailable ); - 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 ); - hr = update_current_row( recordset ); if (FAILED(hr)) return hr; cache_release( recordset ); - if (!recordset->hacc_empty) - { - hr = IAccessor_CreateAccessor( recordset->accessor, DBACCESSOR_ROWDATA, - 0, NULL, 0, &recordset->hacc_empty, NULL ); - if (FAILED(hr) || !recordset->hacc_empty) - return MAKE_ADO_HRESULT( adErrNoCurrentRecord ); - } + hr = get_accessor( recordset, &field_list, &hacc ); + if (FAILED(hr)) return MAKE_ADO_HRESULT( adErrNoCurrentRecord ); - hr = IAccessor_AddRefAccessor( recordset->accessor, recordset->hacc_empty, &refcount ); - if (FAILED(hr)) - return MAKE_ADO_HRESULT( adErrNoCurrentRecord ); hr = IRowsetChange_InsertRow( recordset->rowset_change, 0, - recordset->hacc_empty, NULL, &recordset->current_row ); - IAccessor_ReleaseAccessor( recordset->accessor, recordset->hacc_empty, &refcount ); + hacc, NULL, &recordset->current_row ); + IAccessor_ReleaseAccessor( recordset->accessor, hacc, &refcount ); if (FAILED(hr)) return MAKE_ADO_HRESULT( adErrNoCurrentRecord ); recordset->is_bof = recordset->is_eof = FALSE; @@ -3510,6 +3624,16 @@ static const ADORecordsetConstructionVtbl rsconstruction_vtbl = rsconstruction_put_RowPosition }; +static int hacc_cmp( const void *key, const struct rb_entry *entry ) +{ + const struct hacc_cache_elem *elem = (const struct hacc_cache_elem *)entry; + const struct hacc_cache_key *k = key; + + if (k->len < elem->key.len) return -1; + if (k->len > elem->key.len) return 1; + return memcmp( k->data, elem->key.data, k->len * sizeof(k->data[0]) ); +} + HRESULT Recordset_create( void **obj ) { struct recordset *recordset; @@ -3530,6 +3654,7 @@ HRESULT Recordset_create( void **obj ) recordset->cache.rows = malloc( sizeof(*recordset->cache.rows) ); recordset->max_records = 0; Fields_create( recordset ); + rb_init( &recordset->hacc_cache, hacc_cmp); *obj = &recordset->Recordset_iface; TRACE( "returning iface %p\n", *obj ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9691
From: Piotr Caban <piotr(a)codeweavers.com> --- dlls/msado15/rowset.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/dlls/msado15/rowset.c b/dlls/msado15/rowset.c index 589bbae66d8..937b14bd62d 100644 --- a/dlls/msado15/rowset.c +++ b/dlls/msado15/rowset.c @@ -736,16 +736,11 @@ static HRESULT WINAPI rowset_change_InsertRow(IRowsetChange *iface, HCHAPTER res { struct rowset *rowset = impl_from_IRowsetChange(iface); struct data *val; + HRESULT hr; int i; TRACE("%p, %Iu, %Id, %p, %p\n", rowset, reserved, accessor, data, row); - if (data) - { - FIXME("setting data not implemented\n"); - return E_NOTIMPL; - } - if (rowset->row_cnt == rowset->rows_alloc) { int rows_alloc = max(8, max(rowset->row_cnt + 1, rowset->rows_alloc * 2)); @@ -770,6 +765,12 @@ static HRESULT WINAPI rowset_change_InsertRow(IRowsetChange *iface, HCHAPTER res val->status = DBSTATUS_E_UNAVAILABLE; } + if (data) + { + hr = rowset_change_SetData(iface, rowset->row_cnt + 1, accessor, data); + if (FAILED(hr)) return hr; + } + rowset->row_cnt++; if (row) *row = rowset->row_cnt; return S_OK; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9691
From: Piotr Caban <piotr(a)codeweavers.com> --- dlls/msado15/recordset.c | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/dlls/msado15/recordset.c b/dlls/msado15/recordset.c index 1c5c7cbe533..6737f8240ab 100644 --- a/dlls/msado15/recordset.c +++ b/dlls/msado15/recordset.c @@ -2293,10 +2293,19 @@ static HRESULT WINAPI recordset_AddNew( _Recordset *iface, VARIANT field_list, V DBREFCOUNT refcount; HACCESSOR hacc; HRESULT hr; + void *data; TRACE( "%p, %s, %s\n", recordset, debugstr_variant(&field_list), debugstr_variant(&values) ); - if (V_VT(&field_list) != VT_ERROR) - FIXME( "ignoring field list and values\n" ); + + if ((V_VT(&field_list) & VT_ARRAY) != (V_VT(&values) & VT_ARRAY)) + return MAKE_ADO_HRESULT( adErrInvalidArgument ); + if (V_VT(&field_list) & VT_ARRAY) + { + if (V_ARRAY(&field_list)->rgsabound[0].cElements != V_ARRAY(&values)->rgsabound[0].cElements || + V_VT(&field_list) != (VT_ARRAY | VT_VARIANT) || + V_VT(&values) != (VT_ARRAY | VT_VARIANT)) + return MAKE_ADO_HRESULT( adErrInvalidArgument ); + } if (recordset->state == adStateClosed) return MAKE_ADO_HRESULT( adErrObjectClosed ); @@ -2317,8 +2326,15 @@ static HRESULT WINAPI recordset_AddNew( _Recordset *iface, VARIANT field_list, V hr = get_accessor( recordset, &field_list, &hacc ); if (FAILED(hr)) return MAKE_ADO_HRESULT( adErrNoCurrentRecord ); + if (V_VT(&field_list) == VT_ERROR) + data = NULL; + else if (V_VT(&values) & VT_ARRAY) + data = V_ARRAY(&values)->pvData; + else + data = &values; + hr = IRowsetChange_InsertRow( recordset->rowset_change, 0, - hacc, NULL, &recordset->current_row ); + hacc, data, &recordset->current_row ); IAccessor_ReleaseAccessor( recordset->accessor, hacc, &refcount ); if (FAILED(hr)) return MAKE_ADO_HRESULT( adErrNoCurrentRecord ); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9691
From: Piotr Caban <piotr(a)codeweavers.com> --- dlls/msado15/recordset.c | 41 ++++++++++++++++++++++++++++++++++-- dlls/msado15/tests/msado15.c | 19 ++++++++++++++--- 2 files changed, 55 insertions(+), 5 deletions(-) diff --git a/dlls/msado15/recordset.c b/dlls/msado15/recordset.c index 6737f8240ab..aab91a58a42 100644 --- a/dlls/msado15/recordset.c +++ b/dlls/msado15/recordset.c @@ -2700,16 +2700,53 @@ static HRESULT WINAPI recordset_Update( _Recordset *iface, VARIANT fields, VARIA struct recordset *recordset = impl_from_Recordset( iface ); DBPENDINGSTATUS pending_status; DBROWSTATUS *status; + HACCESSOR hacc; HRESULT hr; + void *data; HROW *row; TRACE( "%p, %s, %s\n", iface, debugstr_variant(&fields), debugstr_variant(&values) ); - if (V_VT(&fields) != VT_ERROR) - FIXME( "ignoring field list and values\n" ); + + if ((V_VT(&fields) & VT_ARRAY) != (V_VT(&values) & VT_ARRAY)) + return MAKE_ADO_HRESULT( adErrInvalidArgument ); + if (V_VT(&fields) & VT_ARRAY) + { + if (V_ARRAY(&fields)->rgsabound[0].cElements != V_ARRAY(&values)->rgsabound[0].cElements || + V_VT(&fields) != (VT_ARRAY | VT_VARIANT) || + V_VT(&values) != (VT_ARRAY | VT_VARIANT)) + return MAKE_ADO_HRESULT( adErrInvalidArgument ); + } if (recordset->state == adStateClosed) return MAKE_ADO_HRESULT( adErrObjectClosed ); if (!recordset->current_row) return MAKE_ADO_HRESULT( adErrNoCurrentRecord ); + if (V_VT(&fields) != VT_ERROR) + { + 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 ); + + hr = get_accessor( recordset, &fields, &hacc ); + if (FAILED(hr)) return hr; + + if (V_VT(&fields) == VT_ERROR) + data = NULL; + else if (V_VT(&values) & VT_ARRAY) + data = V_ARRAY(&values)->pvData; + else + data = &values; + + hr = IRowsetChange_SetData(recordset->rowset_change, recordset->current_row, hacc, data); + IAccessor_ReleaseAccessor( recordset->accessor, hacc, NULL ); + if (FAILED(hr)) return hr; + } + if (recordset->lock_type != adLockPessimistic && recordset->lock_type != adLockOptimistic) return S_OK; diff --git a/dlls/msado15/tests/msado15.c b/dlls/msado15/tests/msado15.c index 67609d9bd8f..5276e1113b2 100644 --- a/dlls/msado15/tests/msado15.c +++ b/dlls/msado15/tests/msado15.c @@ -918,7 +918,7 @@ static HRESULT WINAPI rowset_update_SetData(IRowsetUpdate *iface, CHECK_EXPECT(rowset_update_SetData); - ok(row == 1, "row = %Id\n", row); + ok(row == 1 || row == 10, "row = %Id\n", row); ok(accessor, "accessor = 0\n"); ok(data != NULL, "data = NULL\n"); @@ -2069,6 +2069,21 @@ static void test_ADORecordsetConstruction(BOOL exact_scroll) hr = _Recordset_Update( recordset, missing, missing ); ok( hr == S_OK, "got %08lx\n", hr ); + V_VT(&index) = VT_I4; + V_I4(&index) = 0; + V_VT(&v) = VT_I4; + V_I4(&v) = 123; + SET_EXPECT(accessor_CreateAccessor); + SET_EXPECT(accessor_AddRefAccessor); + SET_EXPECT(rowset_update_SetData); + SET_EXPECT(accessor_ReleaseAccessor); + hr = _Recordset_Update( recordset, index, v ); + ok( hr == S_OK, "got %08lx\n", hr ); + CHECK_CALLED(accessor_CreateAccessor); + CHECK_CALLED(accessor_AddRefAccessor); + CHECK_CALLED(rowset_update_SetData); + CHECK_CALLED(accessor_ReleaseAccessor); + SET_EXPECT(rowset_update_GetRowStatus); SET_EXPECT(rowset_update_Undo); SET_EXPECT(rowset_AddRefRows); @@ -2089,7 +2104,6 @@ static void test_ADORecordsetConstruction(BOOL exact_scroll) V_VT(&v) = VT_BSTR; V_BSTR(&v) = SysAllocString( L"Column1 = 1" ); SET_EXPECT(rowset_ReleaseRows); - SET_EXPECT(accessor_CreateAccessor); SET_EXPECT(accessor_AddRefAccessor); SET_EXPECT(rowset_view_CreateView); SET_EXPECT(view_filter_SetFilter); @@ -2108,7 +2122,6 @@ static void test_ADORecordsetConstruction(BOOL exact_scroll) hr = _Recordset_put_Filter( recordset, v ); todo_wine ok( hr == S_OK, "got %08lx\n", hr ); todo_wine CHECK_CALLED(rowset_ReleaseRows); - todo_wine CHECK_CALLED(accessor_CreateAccessor); todo_wine CHECK_CALLED(accessor_AddRefAccessor); todo_wine CHECK_CALLED(rowset_view_CreateView); todo_wine CHECK_CALLED(view_filter_SetFilter); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9691
participants (2)
-
Piotr Caban -
Piotr Caban (@piotr)