From: Alistair Leslie-Hughes leslie_alistair@hotmail.com
--- dlls/msado15/recordset.c | 239 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 238 insertions(+), 1 deletion(-)
diff --git a/dlls/msado15/recordset.c b/dlls/msado15/recordset.c index c263afe55ab..13807aa1c5a 100644 --- a/dlls/msado15/recordset.c +++ b/dlls/msado15/recordset.c @@ -52,6 +52,7 @@ struct recordset VARIANT filter;
DBTYPE *columntypes; + HACCESSOR *haccessors; };
struct fields @@ -1204,6 +1205,10 @@ static void close_recordset( struct recordset *recordset ) { ULONG row, col, col_count; ULONG i; + IAccessor *accessor; + + if (recordset->haccessors) + IRowset_QueryInterface(recordset->row_set, &IID_IAccessor, (void**)&accessor);
if ( recordset->row_set ) IRowset_Release( recordset->row_set ); recordset->row_set = NULL; @@ -1220,8 +1225,17 @@ static void close_recordset( struct recordset *recordset ) struct field *field = impl_from_Field( recordset->fields->field[i] ); field->recordset = NULL; Field_Release(&field->Field_iface); + + if (recordset->haccessors) + IAccessor_ReleaseAccessor(accessor, recordset->haccessors[i], NULL); }
+ if (recordset->haccessors) + { + IAccessor_Release(accessor); + free(recordset->haccessors); + recordset->haccessors = NULL; + } recordset->fields->count = 0; Fields_Release( &recordset->fields->Fields_iface ); recordset->fields = NULL; @@ -1686,6 +1700,7 @@ static HRESULT create_bindings(IUnknown *rowset, struct recordset *recordset, DB { HRESULT hr; IColumnsInfo *columninfo; + IAccessor *accessor; DBORDINAL columns; DBCOLUMNINFO *colinfo; OLECHAR *stringsbuffer; @@ -1698,6 +1713,13 @@ static HRESULT create_bindings(IUnknown *rowset, struct recordset *recordset, DB if (FAILED(hr)) return hr;
+ hr = IUnknown_QueryInterface(rowset, &IID_IAccessor, (void**)&accessor); + if (FAILED(hr)) + { + IColumnsInfo_Release(columninfo); + return hr; + } + hr = IColumnsInfo_GetColumnInfo(columninfo, &columns, &colinfo, &stringsbuffer); if (SUCCEEDED(hr)) { @@ -1706,6 +1728,7 @@ static HRESULT create_bindings(IUnknown *rowset, struct recordset *recordset, DB offset = 1;
recordset->columntypes = malloc(sizeof(DBTYPE) * columns); + recordset->haccessors = calloc(1, sizeof(HACCESSOR) * columns );
/* Do one allocation for the bindings and append the DBOBJECTS to the end. * This is to save on multiple allocations vs a little bit of extra memory. @@ -1769,6 +1792,10 @@ static HRESULT create_bindings(IUnknown *rowset, struct recordset *recordset, DB bindings[i].obStatus = offset + sizeof(DBBYTEOFFSET);
offset += sizeof(DBBYTEOFFSET) + sizeof(DBBYTEOFFSET); + + hr = IAccessor_CreateAccessor(accessor, DBACCESSOR_ROWDATA, 1, &bindings[i], 0, &recordset->haccessors[i], NULL); + if (FAILED(hr)) + FIXME("IAccessor_CreateAccessor Failed 0x%0lx\n", hr); }
*size = offset; @@ -1778,11 +1805,205 @@ static HRESULT create_bindings(IUnknown *rowset, struct recordset *recordset, DB CoTaskMemFree(stringsbuffer); }
+ IAccessor_Release(accessor); + IColumnsInfo_Release(columninfo);
return hr; }
+static HRESULT load_all_recordset_data(struct recordset *recordset, IUnknown *rowset, DBBINDING *bindings, + DBBYTEOFFSET datasize) +{ + IRowset *rowset2; + DBORDINAL columns; + HRESULT hr; + DBCOUNTITEM obtained; + HROW *row = NULL; + int datarow = 0, datacol; + char *data; + + columns = get_column_count(recordset); + + /* Create the data array */ + if (!resize_recordset( recordset, recordset->count )) + { + WARN("Failed to resize recordset\n"); + return E_OUTOFMEMORY; + } + + hr = IUnknown_QueryInterface(rowset, &IID_IRowset, (void**)&rowset2); + if (FAILED(hr)) + { + WARN("Failed to get IRowset interface (0x%08lx)\n", hr); + return hr; + } + + data = malloc (datasize); + if (!data) + { + ERR("Failed to allocate row data (%Iu)\n", datasize); + IRowset_Release(rowset2); + return E_OUTOFMEMORY; + } + + hr = IRowset_GetNextRows(rowset2, 0, 0, 1, &obtained, &row); + while (hr == S_OK) + { + VARIANT copy; + + for (datacol = 0; datacol < columns; datacol++) + { + hr = IRowset_GetData(rowset2, *row, recordset->haccessors[datacol], data); + if (FAILED(hr)) + { + ERR("GetData Failed on Column %d (0x%08lx), status %Id\n", datacol, hr, + *(DBBYTEOFFSET*)(data + bindings[datacol].obStatus)); + break; + } + + VariantInit(©); + + if ( *(DBBYTEOFFSET*)(data + bindings[datacol].obStatus) == DBSTATUS_S_ISNULL) + { + V_VT(©) = VT_NULL; + goto writedata; + } + + /* For most cases DBTYPE_* = VT_* type */ + V_VT(©) = bindings[datacol].wType; + switch(bindings[datacol].wType) + { + case DBTYPE_IUNKNOWN: + { + ISequentialStream *seq; + char unkdata[2048]; + ULONG size = 4096, dataRead = 0, total = 0; + char *buffer = malloc(size), *p = buffer; + HRESULT hr2; + + /* + * Cast directly to the object we specified in our bindings. As this object + * is referenced counted in some case and will error in GetData if the object + * hasn't been released. + */ + seq = *(ISequentialStream**)(data + bindings[datacol].obValue); + TRACE("Reading DBTYPE_IUNKNOWN %p\n", seq); + + do + { + dataRead = 0; + hr2 = ISequentialStream_Read(seq, unkdata, sizeof(unkdata), &dataRead); + if (FAILED(hr2) || !dataRead) break; + + total += dataRead; + memcpy(p, unkdata, dataRead); + p += dataRead; + if (total == size) + { + size *= 2; /* Double buffer */ + buffer = realloc(buffer, size); + p = buffer + total; + } + } while(hr2 == S_OK); + + if (recordset->columntypes[datacol] == DBTYPE_WSTR) + { + V_VT(©) = VT_BSTR; + V_BSTR(©) = SysAllocStringLen( (WCHAR*)buffer, total / sizeof(WCHAR) ); + } + else if (recordset->columntypes[datacol] == DBTYPE_BYTES) + { + SAFEARRAYBOUND sab; + + sab.lLbound = 0; + sab.cElements = total; + + V_VT(©) = (VT_ARRAY|VT_UI1); + V_ARRAY(©) = SafeArrayCreate(VT_UI1, 1, &sab); + + memcpy( (BYTE*)V_ARRAY(©)->pvData, buffer, total); + } + else + { + FIXME("Unsupported conversion (%d)\n", recordset->columntypes[datacol]); + V_VT(©) = VT_NULL; + } + + free(buffer); + ISequentialStream_Release(seq); + + break; + } + case DBTYPE_R4: + V_R4(©) = *(float*)(data + bindings[datacol].obValue); + break; + case DBTYPE_R8: + V_R8(©) = *(DOUBLE*)(data + bindings[datacol].obValue); + break; + case DBTYPE_I8: + V_VT(©) = VT_I8; + V_I8(©) = *(LONGLONG*)(data + bindings[datacol].obValue); + break; + case DBTYPE_I4: + V_I4(©) = *(LONG*)(data + bindings[datacol].obValue); + break; + case DBTYPE_WSTR: + { + V_VT(©) = VT_BSTR; + V_BSTR(©) = SysAllocString( (WCHAR*)(data + bindings[datacol].obValue) ); + break; + } + case DBTYPE_DBTIMESTAMP: + { + SYSTEMTIME st; + DBTIMESTAMP *ts = (DBTIMESTAMP *)(data + bindings[datacol].obValue); + DATE d; + + V_VT(©) = VT_DATE; + + st.wYear = ts->year; + st.wMonth = ts->month; + st.wDay = ts->day; + st.wHour = ts->hour; + st.wMinute = ts->minute; + st.wSecond = ts->second; + st.wMilliseconds = ts->fraction/1000000; + hr = (SystemTimeToVariantTime(&st, &d) ? S_OK : E_FAIL); + + V_DATE(©) = d; + break; + } + default: + V_I2(©) = 0; + FIXME("Unknown Type %d\n", bindings[datacol].wType); + } + +writedata: + VariantInit( &recordset->data[datarow * columns + datacol] ); + if ((hr = VariantCopy( &recordset->data[datarow * columns + datacol] , ©)) != S_OK) + { + ERR("Column %d copy failed. Data %s\n", datacol, debugstr_variant(©)); + } + + VariantClear(©); + } + + datarow++; + + hr = IRowset_ReleaseRows(rowset2, 1, row, NULL, NULL, NULL); + if (FAILED(hr)) + ERR("Failed to ReleaseRows 0x%08lx\n", hr); + + hr = IRowset_GetNextRows(rowset2, 0, 0, 1, &obtained, &row); + } + + free(data); + IRowset_Release(rowset2); + + return S_OK; +} + static HRESULT WINAPI recordset_Open( _Recordset *iface, VARIANT source, VARIANT active_connection, CursorTypeEnum cursor_type, LockTypeEnum lock_type, LONG options ) { @@ -1796,7 +2017,7 @@ static HRESULT WINAPI recordset_Open( _Recordset *iface, VARIANT source, VARIANT DBBINDING *bindings; DBBYTEOFFSET datasize;
- FIXME( "%p, %s, %s, %d, %d, %ld Semi-stub\n", recordset, debugstr_variant(&source), debugstr_variant(&active_connection), + TRACE( "%p, %s, %s, %d, %d, %ld\n", recordset, debugstr_variant(&source), debugstr_variant(&active_connection), cursor_type, lock_type, options );
if (recordset->state == adStateOpen) return MAKE_ADO_HRESULT( adErrObjectOpen ); @@ -1860,6 +2081,21 @@ static HRESULT WINAPI recordset_Open( _Recordset *iface, VARIANT source, VARIANT recordset->count = affected; recordset->index = affected ? 0 : -1;
+ /* + * We can safely just return with an empty recordset here + */ + if (affected > 0) + { + hr = load_all_recordset_data(recordset, rowset, bindings, datasize); + if (FAILED(hr)) + { + WARN("Failed to load all recordset data (%lx)\n", hr); + CoTaskMemFree(bindings); + IUnknown_Release(rowset); + return hr; + } + } + CoTaskMemFree(bindings);
ADORecordsetConstruction_put_Rowset(&recordset->ADORecordsetConstruction_iface, rowset); @@ -2476,6 +2712,7 @@ HRESULT Recordset_create( void **obj ) recordset->editmode = adEditNone; VariantInit( &recordset->filter ); recordset->columntypes = NULL; + recordset->haccessors = NULL;
*obj = &recordset->Recordset_iface; TRACE( "returning iface %p\n", *obj );