blob: c1cee127e8492e052441122e9ded3cfa72f58990 [file] [log] [blame]
/* OLE DB Row Position library
*
* Copyright 2013 Nikolay Sivov
*
* 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
*/
#define COBJMACROS
#include "windef.h"
#include "ole2.h"
#include "olectl.h"
#include "oledb.h"
#include "oledberr.h"
#include "oledb_private.h"
#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(oledb);
typedef struct rowpos rowpos;
typedef struct
{
IConnectionPoint IConnectionPoint_iface;
rowpos *container;
IRowPositionChange **sinks;
DWORD sinks_size;
} rowpos_cp;
struct rowpos
{
IRowPosition IRowPosition_iface;
IConnectionPointContainer IConnectionPointContainer_iface;
LONG ref;
IRowset *rowset;
IChapteredRowset *chrst;
HROW row;
HCHAPTER chapter;
DBPOSITIONFLAGS flags;
BOOL cleared;
rowpos_cp cp;
};
static void rowposchange_cp_destroy(rowpos_cp*);
static inline rowpos *impl_from_IRowPosition(IRowPosition *iface)
{
return CONTAINING_RECORD(iface, rowpos, IRowPosition_iface);
}
static inline rowpos *impl_from_IConnectionPointContainer(IConnectionPointContainer *iface)
{
return CONTAINING_RECORD(iface, rowpos, IConnectionPointContainer_iface);
}
static inline rowpos_cp *impl_from_IConnectionPoint(IConnectionPoint *iface)
{
return CONTAINING_RECORD(iface, rowpos_cp, IConnectionPoint_iface);
}
static HRESULT rowpos_fireevent(rowpos *rp, DBREASON reason, DBEVENTPHASE phase)
{
BOOL cant_deny = phase == DBEVENTPHASE_FAILEDTODO || phase == DBEVENTPHASE_SYNCHAFTER;
HRESULT hr = S_OK;
DWORD i;
for (i = 0; i < rp->cp.sinks_size; i++)
if (rp->cp.sinks[i])
{
hr = IRowPositionChange_OnRowPositionChange(rp->cp.sinks[i], reason, phase, cant_deny);
if (phase == DBEVENTPHASE_FAILEDTODO) return DB_E_CANCELED;
if (hr != S_OK) return hr;
}
return hr;
}
static void rowpos_clearposition(rowpos *rp)
{
if (!rp->cleared)
{
if (rp->rowset)
IRowset_ReleaseRows(rp->rowset, 1, &rp->row, NULL, NULL, NULL);
if (rp->chrst)
IChapteredRowset_ReleaseChapter(rp->chrst, rp->chapter, NULL);
}
rp->row = DB_NULL_HROW;
rp->chapter = DB_NULL_HCHAPTER;
rp->flags = DBPOSITION_NOROW;
}
static HRESULT WINAPI rowpos_QueryInterface(IRowPosition* iface, REFIID riid, void **obj)
{
rowpos *This = impl_from_IRowPosition(iface);
TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), obj);
*obj = NULL;
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IRowPosition))
{
*obj = iface;
}
else if (IsEqualIID(riid, &IID_IConnectionPointContainer))
{
*obj = &This->IConnectionPointContainer_iface;
}
else
{
FIXME("interface %s not implemented\n", debugstr_guid(riid));
return E_NOINTERFACE;
}
IRowPosition_AddRef(iface);
return S_OK;
}
static ULONG WINAPI rowpos_AddRef(IRowPosition* iface)
{
rowpos *This = impl_from_IRowPosition(iface);
ULONG ref = InterlockedIncrement(&This->ref);
TRACE("(%p)->(%d)\n", This, ref);
return ref;
}
static ULONG WINAPI rowpos_Release(IRowPosition* iface)
{
rowpos *This = impl_from_IRowPosition(iface);
LONG ref = InterlockedDecrement(&This->ref);
TRACE("(%p)->(%d)\n", This, ref);
if (ref == 0)
{
if (This->rowset) IRowset_Release(This->rowset);
if (This->chrst) IChapteredRowset_Release(This->chrst);
rowposchange_cp_destroy(&This->cp);
heap_free(This);
}
return ref;
}
static HRESULT WINAPI rowpos_ClearRowPosition(IRowPosition* iface)
{
rowpos *This = impl_from_IRowPosition(iface);
HRESULT hr;
TRACE("(%p)\n", This);
if (!This->rowset) return E_UNEXPECTED;
hr = rowpos_fireevent(This, DBREASON_ROWPOSITION_CLEARED, DBEVENTPHASE_OKTODO);
if (hr != S_OK)
return rowpos_fireevent(This, DBREASON_ROWPOSITION_CLEARED, DBEVENTPHASE_FAILEDTODO);
hr = rowpos_fireevent(This, DBREASON_ROWPOSITION_CLEARED, DBEVENTPHASE_ABOUTTODO);
if (hr != S_OK)
return rowpos_fireevent(This, DBREASON_ROWPOSITION_CLEARED, DBEVENTPHASE_FAILEDTODO);
rowpos_clearposition(This);
This->cleared = TRUE;
return S_OK;
}
static HRESULT WINAPI rowpos_GetRowPosition(IRowPosition *iface, HCHAPTER *chapter,
HROW *row, DBPOSITIONFLAGS *flags)
{
rowpos *This = impl_from_IRowPosition(iface);
TRACE("(%p)->(%p %p %p)\n", This, chapter, row, flags);
*chapter = This->chapter;
*row = This->row;
*flags = This->flags;
if (!This->rowset) return E_UNEXPECTED;
return S_OK;
}
static HRESULT WINAPI rowpos_GetRowset(IRowPosition *iface, REFIID riid, IUnknown **rowset)
{
rowpos *This = impl_from_IRowPosition(iface);
TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), rowset);
if (!This->rowset) return E_UNEXPECTED;
return IRowset_QueryInterface(This->rowset, riid, (void**)rowset);
}
static HRESULT WINAPI rowpos_Initialize(IRowPosition *iface, IUnknown *rowset)
{
rowpos *This = impl_from_IRowPosition(iface);
HRESULT hr;
TRACE("(%p)->(%p)\n", This, rowset);
if (This->rowset) return DB_E_ALREADYINITIALIZED;
hr = IUnknown_QueryInterface(rowset, &IID_IRowset, (void**)&This->rowset);
if (FAILED(hr)) return hr;
/* this one is optional */
IUnknown_QueryInterface(rowset, &IID_IChapteredRowset, (void**)&This->chrst);
return S_OK;
}
static HRESULT WINAPI rowpos_SetRowPosition(IRowPosition *iface, HCHAPTER chapter,
HROW row, DBPOSITIONFLAGS flags)
{
rowpos *This = impl_from_IRowPosition(iface);
DBREASON reason;
HRESULT hr;
TRACE("(%p)->(%lx %lx %d)\n", This, chapter, row, flags);
if (!This->cleared) return E_UNEXPECTED;
hr = IRowset_AddRefRows(This->rowset, 1, &row, NULL, NULL);
if (FAILED(hr)) return hr;
if (This->chrst)
{
hr = IChapteredRowset_AddRefChapter(This->chrst, chapter, NULL);
if (FAILED(hr))
{
IRowset_ReleaseRows(This->rowset, 1, &row, NULL, NULL, NULL);
return hr;
}
}
reason = This->chrst ? DBREASON_ROWPOSITION_CHAPTERCHANGED : DBREASON_ROWPOSITION_CHANGED;
hr = rowpos_fireevent(This, reason, DBEVENTPHASE_SYNCHAFTER);
if (hr != S_OK)
{
IRowset_ReleaseRows(This->rowset, 1, &row, NULL, NULL, NULL);
if (This->chrst)
IChapteredRowset_ReleaseChapter(This->chrst, chapter, NULL);
return rowpos_fireevent(This, reason, DBEVENTPHASE_FAILEDTODO);
}
else
rowpos_fireevent(This, reason, DBEVENTPHASE_DIDEVENT);
/* previously set chapter and row are released with ClearRowPosition() */
This->chapter = chapter;
This->row = row;
This->flags = flags;
This->cleared = FALSE;
return S_OK;
}
static const struct IRowPositionVtbl rowpos_vtbl =
{
rowpos_QueryInterface,
rowpos_AddRef,
rowpos_Release,
rowpos_ClearRowPosition,
rowpos_GetRowPosition,
rowpos_GetRowset,
rowpos_Initialize,
rowpos_SetRowPosition
};
static HRESULT WINAPI cpc_QueryInterface(IConnectionPointContainer *iface, REFIID riid, void **obj)
{
rowpos *This = impl_from_IConnectionPointContainer(iface);
return IRowPosition_QueryInterface(&This->IRowPosition_iface, riid, obj);
}
static ULONG WINAPI cpc_AddRef(IConnectionPointContainer *iface)
{
rowpos *This = impl_from_IConnectionPointContainer(iface);
return IRowPosition_AddRef(&This->IRowPosition_iface);
}
static ULONG WINAPI cpc_Release(IConnectionPointContainer *iface)
{
rowpos *This = impl_from_IConnectionPointContainer(iface);
return IRowPosition_Release(&This->IRowPosition_iface);
}
static HRESULT WINAPI cpc_EnumConnectionPoints(IConnectionPointContainer *iface, IEnumConnectionPoints **enum_points)
{
rowpos *This = impl_from_IConnectionPointContainer(iface);
FIXME("(%p)->(%p): stub\n", This, enum_points);
return E_NOTIMPL;
}
static HRESULT WINAPI cpc_FindConnectionPoint(IConnectionPointContainer *iface, REFIID riid, IConnectionPoint **point)
{
rowpos *This = impl_from_IConnectionPointContainer(iface);
TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), point);
if (IsEqualIID(riid, &IID_IRowPositionChange))
{
*point = &This->cp.IConnectionPoint_iface;
IConnectionPoint_AddRef(*point);
return S_OK;
}
else
{
FIXME("unsupported riid %s\n", debugstr_guid(riid));
return CONNECT_E_NOCONNECTION;
}
}
static const struct IConnectionPointContainerVtbl rowpos_cpc_vtbl =
{
cpc_QueryInterface,
cpc_AddRef,
cpc_Release,
cpc_EnumConnectionPoints,
cpc_FindConnectionPoint
};
static HRESULT WINAPI rowpos_cp_QueryInterface(IConnectionPoint *iface, REFIID riid, void **obj)
{
rowpos_cp *This = impl_from_IConnectionPoint(iface);
return IConnectionPointContainer_QueryInterface(&This->container->IConnectionPointContainer_iface, riid, obj);
}
static ULONG WINAPI rowpos_cp_AddRef(IConnectionPoint *iface)
{
rowpos_cp *This = impl_from_IConnectionPoint(iface);
return IConnectionPointContainer_AddRef(&This->container->IConnectionPointContainer_iface);
}
static ULONG WINAPI rowpos_cp_Release(IConnectionPoint *iface)
{
rowpos_cp *This = impl_from_IConnectionPoint(iface);
return IConnectionPointContainer_Release(&This->container->IConnectionPointContainer_iface);
}
static HRESULT WINAPI rowpos_cp_GetConnectionInterface(IConnectionPoint *iface, IID *iid)
{
rowpos_cp *This = impl_from_IConnectionPoint(iface);
TRACE("(%p)->(%p)\n", This, iid);
if (!iid) return E_POINTER;
*iid = IID_IRowPositionChange;
return S_OK;
}
static HRESULT WINAPI rowpos_cp_GetConnectionPointContainer(IConnectionPoint *iface, IConnectionPointContainer **container)
{
rowpos_cp *This = impl_from_IConnectionPoint(iface);
TRACE("(%p)->(%p)\n", This, container);
if (!container) return E_POINTER;
*container = &This->container->IConnectionPointContainer_iface;
IConnectionPointContainer_AddRef(*container);
return S_OK;
}
static HRESULT WINAPI rowpos_cp_Advise(IConnectionPoint *iface, IUnknown *unksink, DWORD *cookie)
{
rowpos_cp *This = impl_from_IConnectionPoint(iface);
IRowPositionChange *sink;
HRESULT hr;
DWORD i;
TRACE("(%p)->(%p %p)\n", This, unksink, cookie);
if (!cookie) return E_POINTER;
hr = IUnknown_QueryInterface(unksink, &IID_IRowPositionChange, (void**)&sink);
if (FAILED(hr))
{
FIXME("sink doesn't support IRowPositionChange\n");
return CONNECT_E_CANNOTCONNECT;
}
if (This->sinks)
{
for (i = 0; i < This->sinks_size; i++)
{
if (!This->sinks[i])
break;
}
if (i == This->sinks_size)
{
This->sinks_size *= 2;
This->sinks = heap_realloc_zero(This->sinks, This->sinks_size*sizeof(*This->sinks));
}
}
else
{
This->sinks_size = 10;
This->sinks = heap_alloc_zero(This->sinks_size*sizeof(*This->sinks));
i = 0;
}
This->sinks[i] = sink;
if (cookie) *cookie = i + 1;
return S_OK;
}
static HRESULT WINAPI rowpos_cp_Unadvise(IConnectionPoint *iface, DWORD cookie)
{
rowpos_cp *This = impl_from_IConnectionPoint(iface);
TRACE("(%p)->(%d)\n", This, cookie);
if (!cookie || cookie > This->sinks_size || !This->sinks[cookie-1])
return CONNECT_E_NOCONNECTION;
IRowPositionChange_Release(This->sinks[cookie-1]);
This->sinks[cookie-1] = NULL;
return S_OK;
}
static HRESULT WINAPI rowpos_cp_EnumConnections(IConnectionPoint *iface, IEnumConnections **enum_c)
{
rowpos_cp *This = impl_from_IConnectionPoint(iface);
FIXME("(%p)->(%p): stub\n", This, enum_c);
return E_NOTIMPL;
}
static const struct IConnectionPointVtbl rowpos_cp_vtbl =
{
rowpos_cp_QueryInterface,
rowpos_cp_AddRef,
rowpos_cp_Release,
rowpos_cp_GetConnectionInterface,
rowpos_cp_GetConnectionPointContainer,
rowpos_cp_Advise,
rowpos_cp_Unadvise,
rowpos_cp_EnumConnections
};
static void rowposchange_cp_init(rowpos_cp *cp, rowpos *container)
{
cp->IConnectionPoint_iface.lpVtbl = &rowpos_cp_vtbl;
cp->container = container;
cp->sinks = NULL;
cp->sinks_size = 0;
}
void rowposchange_cp_destroy(rowpos_cp *cp)
{
DWORD i;
for (i = 0; i < cp->sinks_size; i++)
{
if (cp->sinks[i])
IRowPositionChange_Release(cp->sinks[i]);
}
heap_free(cp->sinks);
}
HRESULT create_oledb_rowpos(IUnknown *outer, void **obj)
{
rowpos *This;
TRACE("(%p, %p)\n", outer, obj);
*obj = NULL;
if(outer) return CLASS_E_NOAGGREGATION;
This = heap_alloc(sizeof(*This));
if(!This) return E_OUTOFMEMORY;
This->IRowPosition_iface.lpVtbl = &rowpos_vtbl;
This->IConnectionPointContainer_iface.lpVtbl = &rowpos_cpc_vtbl;
This->ref = 1;
This->rowset = NULL;
This->chrst = NULL;
This->cleared = FALSE;
rowpos_clearposition(This);
rowposchange_cp_init(&This->cp, This);
*obj = &This->IRowPosition_iface;
return S_OK;
}