| /* OLE DB Conversion library |
| * |
| * Copyright 2009 Huw Davies |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA |
| */ |
| |
| #include <stdarg.h> |
| |
| #define COBJMACROS |
| #define NONAMELESSUNION |
| #define NONAMELESSSTRUCT |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "ole2.h" |
| #include "msdadc.h" |
| #include "oledberr.h" |
| |
| #include "oledb_private.h" |
| |
| #include "wine/debug.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(oledb); |
| |
| typedef struct |
| { |
| const struct IDataConvertVtbl *lpVtbl; |
| const struct IDCInfoVtbl *lpDCInfoVtbl; |
| |
| LONG ref; |
| |
| UINT version; /* Set by IDCInfo_SetInfo */ |
| } convert; |
| |
| static inline convert *impl_from_IDataConvert(IDataConvert *iface) |
| { |
| return (convert *)((char*)iface - FIELD_OFFSET(convert, lpVtbl)); |
| } |
| |
| static inline convert *impl_from_IDCInfo(IDCInfo *iface) |
| { |
| return (convert *)((char*)iface - FIELD_OFFSET(convert, lpDCInfoVtbl)); |
| } |
| |
| static HRESULT WINAPI convert_QueryInterface(IDataConvert* iface, |
| REFIID riid, |
| void **obj) |
| { |
| convert *This = impl_from_IDataConvert(iface); |
| TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), obj); |
| |
| *obj = NULL; |
| |
| if(IsEqualIID(riid, &IID_IUnknown) || |
| IsEqualIID(riid, &IID_IDataConvert)) |
| { |
| *obj = iface; |
| } |
| else if(IsEqualIID(riid, &IID_IDCInfo)) |
| { |
| *obj = &This->lpDCInfoVtbl; |
| } |
| else |
| { |
| FIXME("interface %s not implemented\n", debugstr_guid(riid)); |
| return E_NOINTERFACE; |
| } |
| |
| IDataConvert_AddRef(iface); |
| return S_OK; |
| } |
| |
| |
| static ULONG WINAPI convert_AddRef(IDataConvert* iface) |
| { |
| convert *This = impl_from_IDataConvert(iface); |
| TRACE("(%p)\n", This); |
| |
| return InterlockedIncrement(&This->ref); |
| } |
| |
| |
| static ULONG WINAPI convert_Release(IDataConvert* iface) |
| { |
| convert *This = impl_from_IDataConvert(iface); |
| LONG ref; |
| |
| TRACE("(%p)\n", This); |
| |
| ref = InterlockedDecrement(&This->ref); |
| if(ref == 0) |
| { |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| |
| return ref; |
| } |
| |
| static HRESULT WINAPI convert_DataConvert(IDataConvert* iface, |
| DBTYPE wSrcType, DBTYPE wDstType, |
| DBLENGTH cbSrcLength, DBLENGTH *pcbDstLength, |
| void *pSrc, void *pDst, |
| DBLENGTH cbDstMaxLength, |
| DBSTATUS dbsSrcStatus, DBSTATUS *pdbsDstStatus, |
| BYTE bPrecision, BYTE bScale, |
| DBDATACONVERT dwFlags) |
| { |
| convert *This = impl_from_IDataConvert(iface); |
| FIXME("(%p)->(%d, %d, %d, %p, %p, %p, %d, %d, %p, %d, %d, %x): stub\n", This, |
| wSrcType, wDstType, cbSrcLength, pcbDstLength, pSrc, pDst, cbDstMaxLength, |
| dbsSrcStatus, pdbsDstStatus, bPrecision, bScale, dwFlags); |
| |
| return E_NOTIMPL; |
| } |
| |
| static inline WORD get_dbtype_class(DBTYPE type) |
| { |
| switch(type) |
| { |
| case DBTYPE_I2: |
| case DBTYPE_R4: |
| case DBTYPE_R8: |
| case DBTYPE_I1: |
| case DBTYPE_UI1: |
| case DBTYPE_UI2: |
| return DBTYPE_I2; |
| |
| case DBTYPE_I4: |
| case DBTYPE_UI4: |
| return DBTYPE_I4; |
| |
| case DBTYPE_I8: |
| case DBTYPE_UI8: |
| return DBTYPE_I8; |
| |
| case DBTYPE_BSTR: |
| case DBTYPE_STR: |
| case DBTYPE_WSTR: |
| return DBTYPE_BSTR; |
| |
| case DBTYPE_DBDATE: |
| case DBTYPE_DBTIME: |
| case DBTYPE_DBTIMESTAMP: |
| return DBTYPE_DBDATE; |
| } |
| return type; |
| } |
| |
| /* Many src types will convert to this group of dst types */ |
| static inline BOOL common_class(WORD dst_class) |
| { |
| switch(dst_class) |
| { |
| case DBTYPE_EMPTY: |
| case DBTYPE_NULL: |
| case DBTYPE_I2: |
| case DBTYPE_I4: |
| case DBTYPE_BSTR: |
| case DBTYPE_BOOL: |
| case DBTYPE_VARIANT: |
| case DBTYPE_I8: |
| case DBTYPE_CY: |
| case DBTYPE_DECIMAL: |
| case DBTYPE_NUMERIC: |
| return TRUE; |
| } |
| return FALSE; |
| } |
| |
| static inline BOOL array_type(DBTYPE type) |
| { |
| return (type >= DBTYPE_I2 && type <= DBTYPE_UI4); |
| } |
| |
| static HRESULT WINAPI convert_CanConvert(IDataConvert* iface, |
| DBTYPE src_type, DBTYPE dst_type) |
| { |
| convert *This = impl_from_IDataConvert(iface); |
| DBTYPE src_base_type = src_type & 0x1ff; |
| DBTYPE dst_base_type = dst_type & 0x1ff; |
| WORD dst_class = get_dbtype_class(dst_base_type); |
| |
| TRACE("(%p)->(%d, %d)\n", This, src_type, dst_type); |
| |
| if(src_type & DBTYPE_VECTOR || dst_type & DBTYPE_VECTOR) return S_FALSE; |
| |
| if(src_type & DBTYPE_ARRAY) |
| { |
| if(!array_type(src_base_type)) return S_FALSE; |
| if(dst_type & DBTYPE_ARRAY) |
| { |
| if(src_type == dst_type) return S_OK; |
| return S_FALSE; |
| } |
| if(dst_type == DBTYPE_VARIANT) return S_OK; |
| return S_FALSE; |
| } |
| |
| if(dst_type & DBTYPE_ARRAY) |
| { |
| if(!array_type(dst_base_type)) return S_FALSE; |
| if(src_type == DBTYPE_IDISPATCH || src_type == DBTYPE_VARIANT) return S_OK; |
| return S_FALSE; |
| } |
| |
| if(dst_type & DBTYPE_BYREF) |
| if(dst_base_type != DBTYPE_BYTES && dst_base_type != DBTYPE_STR && dst_base_type != DBTYPE_WSTR) |
| return S_FALSE; |
| |
| switch(get_dbtype_class(src_base_type)) |
| { |
| case DBTYPE_EMPTY: |
| if(common_class(dst_class)) return S_OK; |
| switch(dst_class) |
| { |
| case DBTYPE_DATE: |
| case DBTYPE_GUID: |
| return S_OK; |
| default: |
| if(dst_base_type == DBTYPE_DBTIMESTAMP) return S_OK; |
| return S_FALSE; |
| } |
| |
| case DBTYPE_NULL: |
| switch(dst_base_type) |
| { |
| case DBTYPE_NULL: |
| case DBTYPE_VARIANT: return S_OK; |
| default: return S_FALSE; |
| } |
| |
| case DBTYPE_I4: |
| if(dst_base_type == DBTYPE_BYTES) return S_OK; |
| /* fall through */ |
| case DBTYPE_I2: |
| if(dst_base_type == DBTYPE_DATE) return S_OK; |
| /* fall through */ |
| case DBTYPE_DECIMAL: |
| if(common_class(dst_class)) return S_OK; |
| if(dst_class == DBTYPE_DBDATE) return S_OK; |
| return S_FALSE; |
| |
| case DBTYPE_BOOL: |
| if(dst_base_type == DBTYPE_DATE) return S_OK; |
| case DBTYPE_NUMERIC: |
| case DBTYPE_CY: |
| if(common_class(dst_class)) return S_OK; |
| return S_FALSE; |
| |
| case DBTYPE_I8: |
| if(common_class(dst_class)) return S_OK; |
| if(dst_base_type == DBTYPE_BYTES) return S_OK; |
| return S_FALSE; |
| |
| case DBTYPE_DATE: |
| switch(dst_class) |
| { |
| case DBTYPE_EMPTY: |
| case DBTYPE_NULL: |
| case DBTYPE_I2: |
| case DBTYPE_I4: |
| case DBTYPE_BSTR: |
| case DBTYPE_BOOL: |
| case DBTYPE_VARIANT: |
| case DBTYPE_I8: |
| case DBTYPE_DATE: |
| case DBTYPE_DBDATE: |
| return S_OK; |
| default: return S_FALSE; |
| } |
| |
| case DBTYPE_IDISPATCH: |
| case DBTYPE_VARIANT: |
| switch(dst_base_type) |
| { |
| case DBTYPE_IDISPATCH: |
| case DBTYPE_ERROR: |
| case DBTYPE_IUNKNOWN: |
| return S_OK; |
| } |
| /* fall through */ |
| case DBTYPE_BSTR: |
| if(common_class(dst_class)) return S_OK; |
| switch(dst_class) |
| { |
| case DBTYPE_DATE: |
| case DBTYPE_GUID: |
| case DBTYPE_BYTES: |
| case DBTYPE_DBDATE: |
| return S_OK; |
| default: return S_FALSE; |
| } |
| |
| case DBTYPE_ERROR: |
| switch(dst_base_type) |
| { |
| case DBTYPE_BSTR: |
| case DBTYPE_ERROR: |
| case DBTYPE_VARIANT: |
| case DBTYPE_WSTR: |
| return S_OK; |
| default: return S_FALSE; |
| } |
| |
| case DBTYPE_IUNKNOWN: |
| switch(dst_base_type) |
| { |
| case DBTYPE_EMPTY: |
| case DBTYPE_NULL: |
| case DBTYPE_IDISPATCH: |
| case DBTYPE_VARIANT: |
| case DBTYPE_IUNKNOWN: |
| return S_OK; |
| default: return S_FALSE; |
| } |
| |
| case DBTYPE_BYTES: |
| if(dst_class == DBTYPE_I4 || dst_class == DBTYPE_I8) return S_OK; |
| /* fall through */ |
| case DBTYPE_GUID: |
| switch(dst_class) |
| { |
| case DBTYPE_EMPTY: |
| case DBTYPE_NULL: |
| case DBTYPE_BSTR: |
| case DBTYPE_VARIANT: |
| case DBTYPE_GUID: |
| case DBTYPE_BYTES: |
| return S_OK; |
| default: return S_FALSE; |
| } |
| |
| case DBTYPE_DBDATE: |
| switch(dst_class) |
| { |
| case DBTYPE_EMPTY: |
| case DBTYPE_NULL: |
| case DBTYPE_DATE: |
| case DBTYPE_BSTR: |
| case DBTYPE_VARIANT: |
| case DBTYPE_DBDATE: |
| return S_OK; |
| default: return S_FALSE; |
| } |
| |
| } |
| return S_FALSE; |
| } |
| |
| static HRESULT WINAPI convert_GetConversionSize(IDataConvert* iface, |
| DBTYPE wSrcType, DBTYPE wDstType, |
| DBLENGTH *pcbSrcLength, DBLENGTH *pcbDstLength, |
| void *pSrc) |
| { |
| convert *This = impl_from_IDataConvert(iface); |
| FIXME("(%p)->(%d, %d, %p, %p, %p): stub\n", This, wSrcType, wDstType, pcbSrcLength, pcbDstLength, pSrc); |
| |
| return E_NOTIMPL; |
| } |
| |
| static const struct IDataConvertVtbl convert_vtbl = |
| { |
| convert_QueryInterface, |
| convert_AddRef, |
| convert_Release, |
| convert_DataConvert, |
| convert_CanConvert, |
| convert_GetConversionSize |
| }; |
| |
| static HRESULT WINAPI dcinfo_QueryInterface(IDCInfo* iface, REFIID riid, void **obj) |
| { |
| convert *This = impl_from_IDCInfo(iface); |
| |
| return IDataConvert_QueryInterface((IDataConvert *)This, riid, obj); |
| } |
| |
| static ULONG WINAPI dcinfo_AddRef(IDCInfo* iface) |
| { |
| convert *This = impl_from_IDCInfo(iface); |
| |
| return IDataConvert_AddRef((IDataConvert *)This); |
| } |
| |
| static ULONG WINAPI dcinfo_Release(IDCInfo* iface) |
| { |
| convert *This = impl_from_IDCInfo(iface); |
| |
| return IDataConvert_Release((IDataConvert *)This); |
| } |
| |
| static HRESULT WINAPI dcinfo_GetInfo(IDCInfo *iface, ULONG num, DCINFOTYPE types[], DCINFO **info_ptr) |
| { |
| convert *This = impl_from_IDCInfo(iface); |
| ULONG i; |
| DCINFO *infos; |
| |
| TRACE("(%p)->(%d, %p, %p)\n", This, num, types, info_ptr); |
| |
| *info_ptr = infos = CoTaskMemAlloc(num * sizeof(*infos)); |
| if(!infos) return E_OUTOFMEMORY; |
| |
| for(i = 0; i < num; i++) |
| { |
| infos[i].eInfoType = types[i]; |
| VariantInit(&infos[i].vData); |
| |
| switch(types[i]) |
| { |
| case DCINFOTYPE_VERSION: |
| V_VT(&infos[i].vData) = VT_UI4; |
| V_UI4(&infos[i].vData) = This->version; |
| break; |
| } |
| } |
| |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI dcinfo_SetInfo(IDCInfo* iface, ULONG num, DCINFO info[]) |
| { |
| convert *This = impl_from_IDCInfo(iface); |
| ULONG i; |
| HRESULT hr = S_OK; |
| |
| TRACE("(%p)->(%d, %p)\n", This, num, info); |
| |
| for(i = 0; i < num; i++) |
| { |
| switch(info[i].eInfoType) |
| { |
| case DCINFOTYPE_VERSION: |
| if(V_VT(&info[i].vData) != VT_UI4) |
| { |
| FIXME("VERSION with vt %x\n", V_VT(&info[i].vData)); |
| hr = DB_S_ERRORSOCCURRED; |
| break; |
| } |
| This->version = V_UI4(&info[i].vData); |
| break; |
| |
| default: |
| FIXME("Unhandled info type %d (vt %x)\n", info[i].eInfoType, V_VT(&info[i].vData)); |
| } |
| } |
| return hr; |
| } |
| |
| static const struct IDCInfoVtbl dcinfo_vtbl = |
| { |
| dcinfo_QueryInterface, |
| dcinfo_AddRef, |
| dcinfo_Release, |
| dcinfo_GetInfo, |
| dcinfo_SetInfo |
| }; |
| |
| HRESULT create_oledb_convert(IUnknown *outer, void **obj) |
| { |
| convert *This; |
| |
| TRACE("(%p, %p)\n", outer, obj); |
| |
| *obj = NULL; |
| |
| if(outer) return CLASS_E_NOAGGREGATION; |
| |
| This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); |
| if(!This) return E_OUTOFMEMORY; |
| |
| This->lpVtbl = &convert_vtbl; |
| This->lpDCInfoVtbl = &dcinfo_vtbl; |
| This->ref = 1; |
| This->version = 0x110; |
| |
| *obj = &This->lpVtbl; |
| |
| return S_OK; |
| } |