As part of changing internal storage data format, data type conversion is implemented. The patchset also contains some minor changes for bookmarks handling.
From: Piotr Caban piotr@codeweavers.com
--- dlls/msado15/recordset.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-)
diff --git a/dlls/msado15/recordset.c b/dlls/msado15/recordset.c index 9419e18889f..d9c0a1b8163 100644 --- a/dlls/msado15/recordset.c +++ b/dlls/msado15/recordset.c @@ -74,8 +74,8 @@ struct bookmark_data { union { - int i; - SAFEARRAY *sa; + int i4; + BYTE *ptr; } val; DBLENGTH len; DBSTATUS status; @@ -156,6 +156,7 @@ static void cache_release( struct recordset *recordset ) static HRESULT get_bookmark( struct recordset *recordset, HROW row, VARIANT *bookmark ) { struct bookmark_data bookmark_data = { 0 }; + SAFEARRAY *sa; HRESULT hr;
hr = IRowset_GetData(recordset->row_set, row, recordset->bookmark_hacc, &bookmark_data); @@ -164,12 +165,27 @@ static HRESULT get_bookmark( struct recordset *recordset, HROW row, VARIANT *boo if (recordset->bookmark_type == DBTYPE_I4) { V_VT(bookmark) = VT_R8; - V_R8(bookmark) = bookmark_data.val.i; + V_R8(bookmark) = bookmark_data.val.i4; return S_OK; }
+ sa = SafeArrayCreateVector( VT_UI1, 0, bookmark_data.len ); + if (!sa) + { + CoTaskMemFree( bookmark_data.val.ptr ); + return E_OUTOFMEMORY; + } + hr = SafeArrayLock( sa ); + if (SUCCEEDED(hr)) + { + memcpy( sa->pvData, bookmark_data.val.ptr, bookmark_data.len ); + SafeArrayUnlock( sa ); + } + CoTaskMemFree( bookmark_data.val.ptr ); + if (FAILED(hr)) return hr; + V_VT(bookmark) = VT_ARRAY | VT_UI1; - V_ARRAY(bookmark) = bookmark_data.val.sa; + V_ARRAY(bookmark) = sa; return S_OK; }
@@ -3002,7 +3018,7 @@ static void init_bookmark( struct recordset *recordset ) if (colinfo[i].ulColumnSize == sizeof(int)) recordset->bookmark_type = DBTYPE_I4; else - recordset->bookmark_type = DBTYPE_ARRAY | DBTYPE_UI1; + recordset->bookmark_type = DBTYPE_BYREF | DBTYPE_BYTES;
memset(&binding, 0, sizeof(binding)); binding.iOrdinal = colinfo[i].iOrdinal; @@ -3010,7 +3026,7 @@ static void init_bookmark( struct recordset *recordset ) binding.obLength = offsetof( struct bookmark_data, len ); binding.obStatus = offsetof( struct bookmark_data, status ); binding.dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS; - binding.cbMaxLen = recordset->bookmark_type == DBTYPE_I4 ? sizeof(int) : sizeof(SAFEARRAY *); + binding.cbMaxLen = recordset->bookmark_type == DBTYPE_I4 ? sizeof(int) : sizeof(void *); binding.wType = recordset->bookmark_type; binding.bPrecision = colinfo[i].bPrecision; binding.bScale = colinfo[i].bScale;
From: Piotr Caban piotr@codeweavers.com
--- dlls/msado15/recordset.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/dlls/msado15/recordset.c b/dlls/msado15/recordset.c index d9c0a1b8163..f90cb054d11 100644 --- a/dlls/msado15/recordset.c +++ b/dlls/msado15/recordset.c @@ -75,6 +75,7 @@ struct bookmark_data union { int i4; + LONGLONG i8; BYTE *ptr; } val; DBLENGTH len; @@ -169,6 +170,13 @@ static HRESULT get_bookmark( struct recordset *recordset, HROW row, VARIANT *boo return S_OK; }
+ if (recordset->bookmark_type == DBTYPE_I8) + { + V_VT(bookmark) = VT_I8; + V_I8(bookmark) = bookmark_data.val.i8; + return S_OK; + } + sa = SafeArrayCreateVector( VT_UI1, 0, bookmark_data.len ); if (!sa) { @@ -1891,7 +1899,8 @@ static HRESULT WINAPI recordset_put_Bookmark( _Recordset *iface, VARIANT bookmar TRACE( "%p, %s\n", iface, debugstr_variant(&bookmark) );
if (recordset->state == adStateClosed) return MAKE_ADO_HRESULT( adErrObjectClosed ); - if (V_VT(&bookmark) != VT_R8 && V_VT(&bookmark) != (VT_ARRAY | VT_UI1)) + if (V_VT(&bookmark) != VT_R8 && V_VT(&bookmark) != VT_I8 && + V_VT(&bookmark) != (VT_ARRAY | VT_UI1)) return MAKE_ADO_HRESULT( adErrInvalidArgument ); if (!recordset->bookmark_hacc) return MAKE_ADO_HRESULT( adErrFeatureNotAvailable ); @@ -3017,6 +3026,8 @@ static void init_bookmark( struct recordset *recordset )
if (colinfo[i].ulColumnSize == sizeof(int)) recordset->bookmark_type = DBTYPE_I4; + else if (colinfo[i].ulColumnSize == sizeof(INT_PTR)) + recordset->bookmark_type = DBTYPE_I8; else recordset->bookmark_type = DBTYPE_BYREF | DBTYPE_BYTES;
From: Piotr Caban piotr@codeweavers.com
--- dlls/msado15/rowset.c | 263 ++++++++++++++++++++++++----------- dlls/msado15/tests/msado15.c | 18 ++- 2 files changed, 202 insertions(+), 79 deletions(-)
diff --git a/dlls/msado15/rowset.c b/dlls/msado15/rowset.c index ba82d1d3b31..14d7788adf8 100644 --- a/dlls/msado15/rowset.c +++ b/dlls/msado15/rowset.c @@ -29,6 +29,13 @@
WINE_DEFAULT_DEBUG_CHANNEL(msado15);
+struct data +{ + DBSTATUS status; + DBLENGTH length; + BYTE data[1]; +}; + struct rowset { IRowsetExactScroll IRowsetExactScroll_iface; @@ -48,8 +55,10 @@ struct rowset int index; int row_cnt;
- int data_cnt; - VARIANT *data; + int rows_alloc; + int row_size; + int *column_off; + BYTE *data; };
struct accessor @@ -59,6 +68,84 @@ struct accessor DBBINDING bindings[1]; };
+static void dbtype_free(DBTYPE type, void *data) +{ + if (type & DBTYPE_BYREF) + { + void *p = *(void **)data; + + if (p) + { + dbtype_free(type & ~DBTYPE_BYREF, p); + free(p); + } + return; + } + if (type & DBTYPE_ARRAY) + { + SAFEARRAY *sa = *(SAFEARRAY **)data; + SafeArrayDestroy(sa); + return; + } + if (type & DBTYPE_VECTOR) + { + DBVECTOR *vec = *(DBVECTOR **)data; + CoTaskMemFree(vec->ptr); + return; + } + + switch (type) + { + case DBTYPE_BSTR: SysFreeString(*(BSTR *)data); break; + case DBTYPE_IDISPATCH: IDispatch_Release(*(IDispatch **)data); break; + case DBTYPE_VARIANT: VariantClear((VARIANT *)data); break; + case DBTYPE_IUNKNOWN: IUnknown_Release(*(IUnknown **)data); break; + } +} + +static int dbtype_size(DBTYPE type, DBLENGTH max_len) +{ + if (type & DBTYPE_BYREF) return sizeof(void*); + if (type & DBTYPE_ARRAY) return sizeof(void*); + if (type & DBTYPE_VECTOR) return sizeof(DBVECTOR); + + switch (type) + { + case DBTYPE_EMPTY: return 0; + case DBTYPE_NULL: return 0; + case DBTYPE_I2: return sizeof(short); + case DBTYPE_I4: return sizeof(int); + case DBTYPE_R4: return sizeof(float); + case DBTYPE_R8: return sizeof(double); + case DBTYPE_CY: return sizeof(CY); + case DBTYPE_DATE: return sizeof(DATE); + case DBTYPE_BSTR: return sizeof(BSTR); + case DBTYPE_IDISPATCH: return sizeof(IDispatch*); + case DBTYPE_ERROR: return sizeof(SCODE); + case DBTYPE_BOOL: return sizeof(VARIANT_BOOL); + case DBTYPE_VARIANT: return sizeof(VARIANT); + case DBTYPE_IUNKNOWN: return sizeof(IUnknown*); + case DBTYPE_DECIMAL: return sizeof(DECIMAL); + case DBTYPE_I1: return sizeof(char); + case DBTYPE_UI1: return sizeof(char); + case DBTYPE_UI2: return sizeof(short); + case DBTYPE_UI4: return sizeof(int); + case DBTYPE_I8: return sizeof(LONGLONG); + case DBTYPE_UI8: return sizeof(LONGLONG); + case DBTYPE_GUID: return sizeof(GUID); + case DBTYPE_BYTES: return sizeof(BYTE) * max_len; + case DBTYPE_STR: return sizeof(CHAR) * max_len; + case DBTYPE_WSTR: return sizeof(WCHAR) * max_len; + case DBTYPE_NUMERIC: return sizeof(DB_NUMERIC); + case DBTYPE_DBDATE: return sizeof(DBDATE); + case DBTYPE_DBTIME: return sizeof(DBTIME); + case DBTYPE_DBTIMESTAMP: return sizeof(DBTIMESTAMP); + } + + FIXME("unsupported type: %x\n", type); + return 0; +} + static inline struct rowset *impl_from_IRowsetExactScroll(IRowsetExactScroll *iface) { return CONTAINING_RECORD(iface, struct rowset, IRowsetExactScroll_iface); @@ -145,18 +232,26 @@ static ULONG WINAPI rowset_Release(IRowsetExactScroll *iface)
if (!refs) { - int i; + int i, j;
TRACE("destroying %p\n", rowset);
if (rowset->convert) IDataConvert_Release(rowset->convert);
- for (i = 0; i < rowset->data_cnt; i++) - VariantClear(rowset->data + i); + for (i = 0; i < rowset->row_cnt; i++) + { + for (j = 0; j < rowset->columns_cnt; j++) + { + struct data *data = (struct data *)(rowset->data + + i * rowset->row_size + rowset->column_off[j]); + dbtype_free(rowset->columns[j].wType, data->data); + } + } free(rowset->data);
CoTaskMemFree(rowset->columns); CoTaskMemFree(rowset->columns_buf); + free(rowset->column_off); free(rowset); } return refs; @@ -181,11 +276,11 @@ static HRESULT WINAPI rowset_GetData(IRowsetExactScroll *iface, HROW row, HACCES { struct rowset *rowset = impl_from_IRowsetExactScroll(iface); struct accessor *accessor = (struct accessor *)hacc; - DBSTATUS status = DBSTATUS_S_OK; BOOL succ = FALSE, err = FALSE; - HRESULT hr = S_OK; + DBSTATUS status; DBLENGTH len; - int i, idx; + HRESULT hr; + int i;
TRACE("%p, %Id, %Id, %p\n", rowset, row, hacc, data);
@@ -202,43 +297,31 @@ static HRESULT WINAPI rowset_GetData(IRowsetExactScroll *iface, HROW row, HACCES
for (i = 0; i < accessor->bindings_count; i++) { - idx = (row - 1) * rowset->columns_cnt + accessor->bindings[i].iOrdinal; + int col = accessor->bindings[i].iOrdinal; + struct data *val = (struct data *)(rowset->data + + (row - 1) * rowset->row_size + rowset->column_off[col]); + status = DBSTATUS_S_OK; len = 0;
- if (accessor->bindings[i].wType != DBTYPE_VARIANT) - { - hr = IDataConvert_GetConversionSize(rowset->convert, DBTYPE_VARIANT, - accessor->bindings[i].wType, NULL, &len, &rowset->data[idx]); - if (FAILED(hr)) status = DBSTATUS_E_CANTCONVERTVALUE; - } - else len = sizeof(VARIANT); - - if (status != DBSTATUS_S_OK) {} - if (accessor->bindings[i].cbMaxLen < len) - status = DBSTATUS_E_DATAOVERFLOW; - else if (!(accessor->bindings[i].dwPart & DBPART_VALUE)) + if (!(accessor->bindings[i].dwPart & DBPART_VALUE)) status = DBSTATUS_E_BADACCESSOR; - else if (accessor->bindings[i].wType != DBTYPE_VARIANT) + else if (accessor->bindings[i].wType == DBTYPE_VARIANT && val->status == DBSTATUS_E_UNAVAILABLE) { - hr = IDataConvert_DataConvert(rowset->convert, DBTYPE_VARIANT, - accessor->bindings[i].wType, sizeof(VARIANT), NULL, - &rowset->data[idx], (BYTE *)data + accessor->bindings[i].obValue, - accessor->bindings[i].cbMaxLen, DBSTATUS_S_OK, &status, - accessor->bindings[i].bPrecision, accessor->bindings[i].bScale, 0); - if (FAILED(hr) && status == DBSTATUS_S_OK) - status = DBSTATUS_E_CANTCONVERTVALUE; + if (accessor->bindings[i].cbMaxLen < sizeof(VARIANT)) + status = DBSTATUS_E_BADACCESSOR; + else + VariantInit((VARIANT *)val->data); } else { - VARIANT val; - - VariantInit(&val); - hr = VariantCopy(&val, &rowset->data[idx]); + hr = IDataConvert_DataConvert(rowset->convert, rowset->columns[col].wType, + accessor->bindings[i].wType, val->length, &len, + val->data, (BYTE *)data + accessor->bindings[i].obValue, + accessor->bindings[i].cbMaxLen, val->status, &status, + accessor->bindings[i].bPrecision, accessor->bindings[i].bScale, 0); if (FAILED(hr)) - status = DBSTATUS_E_CANTCONVERTVALUE; - else - memcpy((BYTE *)data + accessor->bindings[i].obValue, &val, sizeof(val)); + WARN("data conversion failed\n"); }
if (accessor->bindings[i].dwPart & DBPART_LENGTH) @@ -246,7 +329,9 @@ static HRESULT WINAPI rowset_GetData(IRowsetExactScroll *iface, HROW row, HACCES if (accessor->bindings[i].dwPart & DBPART_STATUS) memcpy((BYTE *)data + accessor->bindings[i].obStatus, &status, sizeof(status));
- if (status == DBSTATUS_S_OK) succ = TRUE; + if (status == DBSTATUS_S_OK || status == DBSTATUS_S_ISNULL || + status == DBSTATUS_S_TRUNCATED || status == DBSTATUS_S_DEFAULT) + succ = TRUE; else err = TRUE; }
@@ -590,45 +675,41 @@ static HRESULT WINAPI rowset_change_SetData(IRowsetChange *iface, HROW row, HACC BOOL err = FALSE, succ = FALSE; DBSTATUS status; DBLENGTH len; - int i, idx; HRESULT hr; + int i;
TRACE("%p, %Id, %Id, %p\n", rowset, row, hacc, data);
if (!accessor->bindings_count) return DB_E_BADACCESSORTYPE; - if (row > rowset->row_cnt) return DB_E_BADROWHANDLE; - for (i = 0; i < accessor->bindings_count; i++) - { - if (accessor->bindings[i].wType != DBTYPE_VARIANT) - { - FIXME("data conversion not implemented\n"); - return E_NOTIMPL; - } - } + if (row < 1 || row > rowset->row_cnt) return DB_E_BADROWHANDLE;
for (i = 0; i < accessor->bindings_count; i++) { - idx = (row - 1) * rowset->columns_cnt + accessor->bindings[i].iOrdinal; - - if (rowset->columns[accessor->bindings[i].iOrdinal].wType != DBTYPE_VARIANT) - FIXME("convert data to column type\n"); + int col = accessor->bindings[i].iOrdinal; + struct data *val = (struct data *)(rowset->data + + (row - 1) * rowset->row_size + rowset->column_off[col]); + DBSTATUS src_status = DBSTATUS_S_OK; + DBLENGTH src_len = 0;
- len = sizeof(VARIANT); status = DBSTATUS_S_OK; - if (accessor->bindings[i].cbMaxLen < len) - status = DBSTATUS_E_DATAOVERFLOW; - else if (accessor->bindings[i].dwPart & DBPART_VALUE) + len = 0; + + if (accessor->bindings[i].dwPart & DBPART_LENGTH) + src_len = *(DBLENGTH *)((BYTE *)data + accessor->bindings[i].obLength); + if (accessor->bindings[i].dwPart & DBPART_STATUS) + src_status = *(DBSTATUS *)((BYTE *)data + accessor->bindings[i].obStatus); + + if (!(accessor->bindings[i].dwPart & DBPART_VALUE)) + status = DBSTATUS_E_BADACCESSOR; + else { - hr = VariantCopy(&rowset->data[idx], (VARIANT *)((BYTE *)data + accessor->bindings[i].obValue)); + hr = IDataConvert_DataConvert(rowset->convert, accessor->bindings[i].wType, + rowset->columns[col].wType, src_len, &len, + (BYTE *)data + accessor->bindings[i].obValue, val->data, + rowset->columns[col].ulColumnSize, src_status, &status, + rowset->columns[col].bPrecision, rowset->columns[col].bScale, 0); if (FAILED(hr)) - { - for (i--; i>=0; i--) - { - if (accessor->bindings[i].dwPart & DBPART_VALUE) - VariantClear((VARIANT *)((BYTE *)data + accessor->bindings[i].obValue)); - } - return hr; - } + WARN("data conversion failed\n"); }
if (accessor->bindings[i].dwPart & DBPART_LENGTH) @@ -636,7 +717,13 @@ static HRESULT WINAPI rowset_change_SetData(IRowsetChange *iface, HROW row, HACC if (accessor->bindings[i].dwPart & DBPART_STATUS) memcpy((BYTE *)data + accessor->bindings[i].obStatus, &status, sizeof(status));
- if (status == DBSTATUS_S_OK) succ = TRUE; + if (status == DBSTATUS_S_OK || status == DBSTATUS_S_ISNULL || + status == DBSTATUS_S_TRUNCATED || status == DBSTATUS_S_DEFAULT) + { + val->status = status; + val->length = len; + succ = TRUE; + } else err = TRUE; }
@@ -648,7 +735,8 @@ static HRESULT WINAPI rowset_change_InsertRow(IRowsetChange *iface, HCHAPTER res HACCESSOR accessor, void *data, HROW *row) { struct rowset *rowset = impl_from_IRowsetChange(iface); - int idx, size; + struct data *val; + int i;
TRACE("%p, %Iu, %Id, %p, %p\n", rowset, reserved, accessor, data, row);
@@ -658,26 +746,31 @@ static HRESULT WINAPI rowset_change_InsertRow(IRowsetChange *iface, HCHAPTER res return E_NOTIMPL; }
- idx = rowset->row_cnt * rowset->columns_cnt; - size = idx + rowset->columns_cnt; - if (size > rowset->data_cnt) + if (rowset->row_cnt == rowset->rows_alloc) { - VARIANT *tmp; + int rows_alloc = max(8, max(rowset->row_cnt + 1, rowset->rows_alloc * 2)); + BYTE *tmp;
- size = max(size, rowset->data_cnt * 2); - size = max(size, 8 * rowset->columns_cnt); - tmp = realloc(rowset->data, size * sizeof(*rowset->data)); + tmp = realloc(rowset->data, rows_alloc * rowset->row_size); if (!tmp) return E_OUTOFMEMORY;
- memset(tmp + rowset->data_cnt, 0, (size - rowset->data_cnt) * sizeof(*rowset->data)); + memset(tmp + rowset->rows_alloc * rowset->row_size, 0, + (rows_alloc - rowset->rows_alloc) * rowset->row_size); rowset->data = tmp; - rowset->data_cnt = size; + rowset->rows_alloc = rows_alloc; }
- rowset->row_cnt++; - V_VT(&rowset->data[idx]) = VT_I4; - V_I4(&rowset->data[idx]) = rowset->row_cnt; + val = (struct data *)(rowset->data + rowset->row_cnt * rowset->row_size); + *(int*)val->data = rowset->row_cnt + 1;
+ for (i = 1; i < rowset->columns_cnt; i++) + { + /* TODO: handle default values */ + val = (struct data *)(rowset->data + rowset->row_cnt * rowset->row_size + rowset->column_off[i]); + val->status = DBSTATUS_E_UNAVAILABLE; + } + + rowset->row_cnt++; if (row) *row = rowset->row_cnt; return S_OK; } @@ -932,6 +1025,7 @@ HRESULT create_mem_rowset(int count, const DBCOLUMNINFO *info, IUnknown **ret) { struct rowset *rowset; HRESULT hr; + int i;
rowset = calloc(1, sizeof(*rowset)); if (!rowset) return E_OUTOFMEMORY; @@ -943,6 +1037,13 @@ HRESULT create_mem_rowset(int count, const DBCOLUMNINFO *info, IUnknown **ret) rowset->IRowsetInfo_iface.lpVtbl = &rowset_info_vtbl; rowset->refs = 1;
+ rowset->column_off = malloc(sizeof(*rowset->column_off) * count); + if (!rowset->column_off) + { + IRowsetExactScroll_Release(&rowset->IRowsetExactScroll_iface); + return E_OUTOFMEMORY; + } + rowset->columns_cnt = count; hr = copy_column_info(&rowset->columns, info, count, &rowset->columns_buf); if (FAILED(hr)) @@ -950,6 +1051,12 @@ HRESULT create_mem_rowset(int count, const DBCOLUMNINFO *info, IUnknown **ret) IRowsetExactScroll_Release(&rowset->IRowsetExactScroll_iface); return E_OUTOFMEMORY; } + for (i = 0; i < count; i++) + { + rowset->column_off[i] = rowset->row_size; + rowset->row_size += offsetof(struct data, + data[dbtype_size(info[i].wType, info[i].ulColumnSize)]); + }
*ret = (IUnknown *)&rowset->IRowsetExactScroll_iface; return S_OK; diff --git a/dlls/msado15/tests/msado15.c b/dlls/msado15/tests/msado15.c index af8d47c7837..594c25697e7 100644 --- a/dlls/msado15/tests/msado15.c +++ b/dlls/msado15/tests/msado15.c @@ -415,6 +415,11 @@ static void test_Recordset(void) hr = Field_put_Value( field, val ); ok( hr == S_OK, "got %08lx\n", hr );
+ V_VT( &val ) = VT_ERROR; + V_ERROR( &val ) = DISP_E_PARAMNOTFOUND; + hr = Field_put_Value( field, val ); + ok( hr == DB_E_ERRORSOCCURRED, "got %08lx\n", hr ); + V_VT( &val ) = VT_ERROR; V_ERROR( &val ) = DISP_E_PARAMNOTFOUND; hr = Field_get_Value( field, &val ); @@ -422,6 +427,17 @@ static void test_Recordset(void) ok( V_VT( &val ) == VT_I4, "got %u\n", V_VT( &val ) ); ok( V_I4( &val ) == -1, "got %ld\n", V_I4( &val ) );
+ V_VT( &val ) = VT_BSTR; + V_BSTR( &val ) = SysAllocString( L"5" ); + hr = Field_put_Value( field, val ); + ok( hr == S_OK, "got %08lx\n", hr ); + VariantClear( &val ); + + hr = Field_get_Value( field, &val ); + ok( hr == S_OK, "got %08lx\n", hr ); + ok( V_VT( &val ) == VT_I4, "got %u\n", V_VT( &val ) ); + ok( V_I4( &val ) == 5, "got %ld\n", V_I4( &val ) ); + /* Update/Cancel doesn't update EditMode when no active connection. */ hr = _Recordset_Update( recordset, missing, missing ); ok( hr == S_OK, "got %08lx\n", hr ); @@ -461,7 +477,7 @@ static void test_Recordset(void) hr = Field_get_Value( field, &val ); ok( hr == S_OK, "got %08lx\n", hr ); ok( V_VT( &val ) == VT_I4, "got %u\n", V_VT( &val ) ); - ok( V_I4( &val ) == -1, "got %ld\n", V_I4( &val ) ); + ok( V_I4( &val ) == 5, "got %ld\n", V_I4( &val ) );
hr = _Recordset_MoveNext( recordset ); ok( hr == S_OK, "got %08lx\n", hr );