msado15: Store all data from provider.
This commit is contained in:
parent
4ae5d2537b
commit
49f7059745
1 changed files with 238 additions and 1 deletions
|
@ -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 );
|
||||
|
|
Loading…
Add table
Reference in a new issue