From: Piotr Caban <piotr@codeweavers.com> --- dlls/msado15/recordset.c | 119 +++++++++++++++++++++++------ dlls/msado15/tests/msado15.c | 144 +++++++++++++++++++++++++++++++++++ 2 files changed, 241 insertions(+), 22 deletions(-) diff --git a/dlls/msado15/recordset.c b/dlls/msado15/recordset.c index b14ffb14341..e83ce92ab9b 100644 --- a/dlls/msado15/recordset.c +++ b/dlls/msado15/recordset.c @@ -28,6 +28,9 @@ #include "oledberr.h" #include "sqlucode.h" +#include "initguid.h" +#include "msdasql.h" + #include "wine/rbtree.h" #include "wine/debug.h" @@ -2521,24 +2524,40 @@ static HRESULT WINAPI recordset_MoveLast( _Recordset *iface ) return cache_get( recordset, FALSE ); } -static inline void set_bool_prop(DBPROP *prop, DBPROPID id, BOOL b) +static inline void add_bool_prop(DBPROPSET *propset, DWORD options, DBPROPID id, BOOL b) { + DBPROP *prop = propset->rgProperties + propset->cProperties; + prop->dwPropertyID = id; + prop->dwOptions = options; V_VT(&prop->vValue) = VT_BOOL; V_BOOL(&prop->vValue) = b ? VARIANT_TRUE : VARIANT_FALSE; + propset->cProperties++; +} + +static inline void add_int_prop(DBPROPSET *propset, DWORD options, DBPROPID id, int v) +{ + DBPROP *prop = propset->rgProperties + propset->cProperties; + + prop->dwPropertyID = id; + prop->dwOptions = options; + V_VT(&prop->vValue) = VT_I4; + V_I4(&prop->vValue) = v; + propset->cProperties++; } static HRESULT get_rowset(struct recordset *recordset, IUnknown *session, BSTR source, IUnknown **rowset) { + DBPROP props[17], provider_props[3]; IDBCreateCommand *create_command; ICommandText *command_text; IOpenRowset *openrowset; + DBPROPSET propsets[2]; DBROWCOUNT affected; - DBPROPSET propset; - DBPROP props[10]; ICommand *cmd; DBID table; HRESULT hr; + int i; hr = IUnknown_QueryInterface(session, &IID_IOpenRowset, (void**)&openrowset); if (FAILED(hr)) @@ -2547,26 +2566,82 @@ static HRESULT get_rowset(struct recordset *recordset, IUnknown *session, BSTR s table.eKind = DBKIND_NAME; table.uName.pwszName = source; - propset.guidPropertySet = DBPROPSET_ROWSET; - propset.cProperties = ARRAY_SIZE(props); - propset.rgProperties = props; + propsets[0].guidPropertySet = DBPROPSET_ROWSET; + propsets[0].cProperties = 0; + propsets[0].rgProperties = props; memset(props, 0, sizeof(props)); - set_bool_prop(props, DBPROP_CANSCROLLBACKWARDS, recordset->cursor_type != adOpenForwardOnly); - set_bool_prop(props + 1, DBPROP_OTHERINSERT, - recordset->cursor_type != adOpenStatic && recordset->cursor_type != adOpenKeyset); - set_bool_prop(props + 2, DBPROP_OTHERUPDATEDELETE, recordset->cursor_type != adOpenStatic); - props[3].dwPropertyID = DBPROP_UPDATABILITY; - V_VT(&props[3].vValue) = VT_I4; - V_I4(&props[3].vValue) = (recordset->lock_type == adLockReadOnly ? 0 : - DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_DELETE | DBPROPVAL_UP_INSERT); - set_bool_prop(props + 4, DBPROP_IColumnsRowset, TRUE); - set_bool_prop(props + 5, DBPROP_IRowsetChange, recordset->lock_type != adLockReadOnly); - set_bool_prop(props + 6, DBPROP_IRowsetLocate, TRUE); - set_bool_prop(props + 7, DBPROP_IRowsetUpdate, recordset->lock_type != adLockReadOnly); - set_bool_prop(props + 8, DBPROP_IRowsetIndex, TRUE); - set_bool_prop(props + 9, DBPROP_IRowsetCurrentIndex, TRUE); - - hr = IOpenRowset_OpenRowset(openrowset, NULL, &table, NULL, &IID_IUnknown, 1, &propset, rowset); + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_IRowset, TRUE); + add_bool_prop(propsets, DBPROPOPTIONS_OPTIONAL, DBPROP_IColumnsRowset, TRUE); + if (recordset->cursor_type == adOpenForwardOnly) + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_OWNUPDATEDELETE, 0); + add_bool_prop(propsets, DBPROPOPTIONS_OPTIONAL, DBPROP_IRowsetIndex, TRUE); + add_bool_prop(propsets, DBPROPOPTIONS_OPTIONAL, DBPROP_IRowsetCurrentIndex, TRUE); + if (recordset->cursor_type == adOpenDynamic) + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_OTHERINSERT, TRUE); + if (recordset->cursor_type == adOpenKeyset) + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_OTHERUPDATEDELETE, TRUE); + if (recordset->cursor_type == adOpenKeyset || recordset->cursor_type == adOpenStatic) + { + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_OWNINSERT, TRUE); + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_OWNUPDATEDELETE, TRUE); + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_CANHOLDROWS, TRUE); + } + if (recordset->cursor_type != adOpenForwardOnly) + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_CANSCROLLBACKWARDS, TRUE); + if (recordset->cursor_type == adOpenKeyset || recordset->cursor_type == adOpenStatic) + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_IRowsetLocate, TRUE); + if (recordset->lock_type != adLockUnspecified) + { + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_IRowsetChange, + recordset->lock_type != adLockReadOnly); + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_IRowsetUpdate, + recordset->lock_type != adLockReadOnly); + add_int_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_UPDATABILITY, + recordset->lock_type == adLockReadOnly ? + 0 : DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_DELETE | DBPROPVAL_UP_INSERT); + } + if (recordset->cursor_type == adOpenKeyset) + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_IRowsetResynch, TRUE); + if (recordset->cursor_type == adOpenKeyset || recordset->cursor_type == adOpenDynamic) + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_REMOVEDELETED, TRUE); + if (recordset->cursor_type != adOpenForwardOnly) + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_CANFETCHBACKWARDS, TRUE); + add_bool_prop(propsets, DBPROPOPTIONS_REQUIRED, DBPROP_ISequentialStream, TRUE); + + propsets[1].guidPropertySet = DBPROPSET_PROVIDERROWSET; + propsets[1].cProperties = 0; + propsets[1].rgProperties = provider_props; + if (recordset->cursor_type == adOpenKeyset) + add_bool_prop(propsets + 1, DBPROPOPTIONS_REQUIRED, KAGPROP_POSITIONONNEWROW, TRUE); + if (recordset->lock_type != adLockReadOnly) + add_int_prop(propsets + 1, DBPROPOPTIONS_REQUIRED, KAGPROP_CONCURRENCY, + KAGPROPVAL_CONCUR_ROWVER | KAGPROPVAL_CONCUR_VALUES | KAGPROPVAL_CONCUR_READ_ONLY); + if (recordset->cursor_type == adOpenForwardOnly) + add_bool_prop(propsets + 1, DBPROPOPTIONS_REQUIRED, KAGPROP_BLOBSONFOCURSOR, TRUE); + add_bool_prop(propsets + 1, DBPROPOPTIONS_REQUIRED, KAGPROP_FORCENOREEXECUTE, TRUE); + + hr = IOpenRowset_OpenRowset(openrowset, NULL, &table, NULL, &IID_IUnknown, 2, propsets, rowset); + if (hr == DB_E_ERRORSOCCURRED) + { + for (i = 0; i < propsets[1].cProperties; i++) + { + if (propsets[1].rgProperties[i].dwPropertyID == KAGPROP_FORCENOREEXECUTE) + { + propsets[1].rgProperties[i].dwOptions = DBPROPOPTIONS_OPTIONAL; + break; + } + } + hr = IOpenRowset_OpenRowset(openrowset, NULL, &table, NULL, &IID_IUnknown, 2, propsets, rowset); + } + if (hr == DB_E_ERRORSOCCURRED) + { + for (i = 0; i < propsets[0].cProperties; i++) + propsets[0].rgProperties[i].dwOptions = DBPROPOPTIONS_OPTIONAL; + for (i = 0; i < propsets[1].cProperties; i++) + propsets[1].rgProperties[i].dwOptions = DBPROPOPTIONS_OPTIONAL; + + hr = IOpenRowset_OpenRowset(openrowset, NULL, &table, NULL, &IID_IUnknown, 2, propsets, rowset); + } if (SUCCEEDED(hr)) { IOpenRowset_Release(openrowset); diff --git a/dlls/msado15/tests/msado15.c b/dlls/msado15/tests/msado15.c index de6a112a99a..03424b9e929 100644 --- a/dlls/msado15/tests/msado15.c +++ b/dlls/msado15/tests/msado15.c @@ -3387,8 +3387,152 @@ static HRESULT WINAPI open_rowset_OpenRowset(IOpenRowset *iface, IUnknown *unk_o DBPROPSET propsets[], IUnknown **rowset) { static struct test_rowset testrowset; + DBPROP *prop; + int i; CHECK_EXPECT(open_rowset_OpenRowset); + ok(!unk_outer, "unk_outer = %p\n", unk_outer); + ok(table_id != NULL, "table_id = NULL\n"); + ok(table_id->eKind == DBKIND_NAME, "table_id->eKind = %ld\n", table_id->eKind); + ok(!wcscmp(table_id->uName.pwszName, L"table name"), "table_id->pwszName = %s\n", + wine_dbgstr_w(table_id->uName.pwszName)); + ok(!index_id, "index_id != NULL\n"); + ok(IsEqualGUID(riid, &IID_IUnknown), "riid = %s\n", wine_dbgstr_guid(riid)); + ok(propsets_count == 2, "propsets_count = %lu\n", propsets_count); + + ok(IsEqualGUID(&propsets[0].guidPropertySet, &DBPROPSET_ROWSET), + "guidPropertySet = %s\n", wine_dbgstr_guid(&propsets[0].guidPropertySet)); + ok(propsets[0].cProperties == 17, "cProperties = %lu\n", propsets[0].cProperties); + for (i = 0; i < propsets[0].cProperties; i++) + { + prop = propsets[0].rgProperties + i; + + winetest_push_context("dwPropertyID = %lx", prop->dwPropertyID); + switch (prop->dwPropertyID) + { + case DBPROP_IRowset: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_IColumnsRowset: + ok(prop->dwOptions == DBPROPOPTIONS_OPTIONAL, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_IRowsetIndex: + ok(prop->dwOptions == DBPROPOPTIONS_OPTIONAL, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_IRowsetCurrentIndex: + ok(prop->dwOptions == DBPROPOPTIONS_OPTIONAL, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_OTHERUPDATEDELETE: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_OWNINSERT: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_OWNUPDATEDELETE: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_CANHOLDROWS: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_CANSCROLLBACKWARDS: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_IRowsetLocate: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_IRowsetChange: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_IRowsetUpdate: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_UPDATABILITY: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_I4, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_I4(&prop->vValue) == (DBPROPVAL_UP_CHANGE | DBPROPVAL_UP_DELETE | DBPROPVAL_UP_INSERT), + "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_IRowsetResynch: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_REMOVEDELETED: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_CANFETCHBACKWARDS: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case DBPROP_ISequentialStream: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + default: + ok(0, "vValue = %s (%lx)\n", wine_dbgstr_variant(&prop->vValue), prop->dwOptions); + } + winetest_pop_context(); + } + + ok(IsEqualGUID(&propsets[1].guidPropertySet, &DBPROPSET_PROVIDERROWSET), + "guidPropertySet = %s\n", wine_dbgstr_guid(&propsets[1].guidPropertySet)); + ok(propsets[1].cProperties == 3, "cProperties = %lu\n", propsets[1].cProperties); + for (i = 0; i < propsets[1].cProperties; i++) + { + prop = propsets[1].rgProperties + i; + + winetest_push_context("dwPropertyID = %lx", prop->dwPropertyID); + switch (prop->dwPropertyID) + { + case KAGPROP_POSITIONONNEWROW: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case KAGPROP_CONCURRENCY: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_I4, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_I4(&prop->vValue) == (KAGPROPVAL_CONCUR_ROWVER | KAGPROPVAL_CONCUR_VALUES | KAGPROPVAL_CONCUR_READ_ONLY), + "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + case KAGPROP_FORCENOREEXECUTE: + ok(prop->dwOptions == DBPROPOPTIONS_REQUIRED, "dwOptions = %ld\n", prop->dwOptions); + ok(V_VT(&prop->vValue) == VT_BOOL, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + ok(V_BOOL(&prop->vValue) == VARIANT_TRUE, "vValue = %s\n", wine_dbgstr_variant(&prop->vValue)); + break; + default: + ok(0, "vValue = %s (%lx)\n", wine_dbgstr_variant(&prop->vValue), prop->dwOptions); + } + winetest_pop_context(); + } testrowset.IRowsetExactScroll_iface.lpVtbl = &rowset_vtbl; testrowset.IRowsetInfo_iface.lpVtbl = &rowset_info; -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/9885