|
|
///////////////////////////////////////////////////////////////////////////////
//
// Copyright (c) Microsoft Corp. All rights reserved.
//
// FILE
//
// SimTable.cpp
//
// SYNOPSIS
//
// This file implements the class CSimpleTable
//
// MODIFICATION HISTORY
//
// 10/31/1997 Original version.
// 02/09/1998 Reorganized some things to make is easier to extend.
// 02/27/1998 Changes to support moving it into the iasutil.lib
// 10/16/1998 Support DBTYPE_WSTR.
//
///////////////////////////////////////////////////////////////////////////////
#include <windows.h>
#include <comdef.h>
#include <iasdefs.h>
#include <iasutil.h>
#include <oledberr.h>
#include <SimTable.h>
//////////
// Stack version of the new operator.
//////////
#define stack_new(obj, num) new (_alloca(sizeof(obj)*num)) obj[num]
///////////////////////////////////////////////////////////////////////////////
//
// STRUCT
//
// DBBinding
//
// DESCRIPTION
//
// This struct extends the DBBINDING struct to provide functionality
// to initialize the struct from a DBCOLUMNINFO struct.
//
///////////////////////////////////////////////////////////////////////////////
struct DBBinding : DBBINDING { //////////
// 'offset' is the offset in bytes of this column's data within the
// row buffer.
//////////
void Initialize(DBCOLUMNINFO& columnInfo, DBBYTEOFFSET& offset) { iOrdinal = columnInfo.iOrdinal; obValue = offset; obLength = offset + columnInfo.ulColumnSize; obStatus = obLength + sizeof(DBLENGTH); pTypeInfo = NULL; pObject = NULL; pBindExt = NULL; dwPart = DBPART_VALUE | DBPART_LENGTH | DBPART_STATUS; eParamIO = DBPARAMIO_NOTPARAM; dwMemOwner = (columnInfo.wType & DBTYPE_BYREF) ? DBMEMOWNER_PROVIDEROWNED : DBMEMOWNER_CLIENTOWNED; cbMaxLen = columnInfo.ulColumnSize; dwFlags = 0; wType = columnInfo.wType; bPrecision = columnInfo.bPrecision; bScale = columnInfo.bScale;
offset = obStatus + sizeof(DBSTATUS); } };
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::CSimpleTable
//
// DESCRIPTION
//
// Constructor.
//
///////////////////////////////////////////////////////////////////////////////
CSimpleTable::CSimpleTable() : numColumns(0), columnInfo(NULL), stringsBuffer(NULL), columnBinding(NULL), readAccess(NULL), buffer(NULL), numRows(0), currentRow(0), endOfRowset(false) { }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::~CSimpleTable
//
// DESCRIPTION
//
// Destructor.
//
///////////////////////////////////////////////////////////////////////////////
CSimpleTable::~CSimpleTable() { Detach(); }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::Attach
//
// DESCRIPTION
//
// This method binds the table object to a new rowset. The previous rowset
// (if any) will be detached.
//
///////////////////////////////////////////////////////////////////////////////
HRESULT CSimpleTable::Attach(IRowset* pRowset) { // Make sure we didn't get a null pointer.
if (!pRowset) { return E_POINTER; }
// Detach the current rowset.
Detach();
// We don't care if this returns an error. It will just prevent
// the user from updating.
pRowset->QueryInterface(IID_IRowsetChange, (void**)&rowsetChange);
//////////
// Get the column information for the table.
//////////
CComPtr<IColumnsInfo> ColumnsInfo; RETURN_ERROR(pRowset->QueryInterface(IID_IColumnsInfo, (void**)&ColumnsInfo));
RETURN_ERROR(ColumnsInfo->GetColumnInfo(&numColumns, &columnInfo, &stringsBuffer));
//////////
// Allocate the per-column data.
//////////
try { columnBinding = new DBBinding[numColumns];
dirty.resize(numColumns); } catch (std::bad_alloc) { return E_OUTOFMEMORY; }
//////////
// Create a binding for each column.
//////////
bufferLength = 0;
for (DBORDINAL i = 0; i < numColumns; ++i) { // Compute the width of the column.
DBLENGTH width = columnInfo[i].ulColumnSize;
// Add room for the null terminator.
if (columnInfo[i].wType == DBTYPE_STR) { width += 1; } else if (columnInfo[i].wType == DBTYPE_WSTR) { width = (width + 1) * sizeof(WCHAR); }
// ulColumnSize is 1 giga byte for columns of type "Memo"
// such as ias.mdb -> Properties.StrVal
// Set the buffer to the maximum size the datastore allows
if (width > PROPERTY_VALUE_LENGTH) { width = PROPERTY_VALUE_LENGTH; }
// Round to an 8-byte boundary (could peek ahead and be more efficient).
width = (width + 7) >> 3 << 3;
columnInfo[i].ulColumnSize = width;
// We're using the pTypeInfo element to store the offset to our data.
// We have to store the offset now, since it will be overwritten by
// DBBinding::Initialize.
columnInfo[i].pTypeInfo = (ITypeInfo*)bufferLength;
columnBinding[i].Initialize(columnInfo[i], bufferLength); }
//////////
// Allocate a buffer for the row data.
//////////
buffer = new (std::nothrow) BYTE[bufferLength];
if (!buffer) { return E_OUTOFMEMORY; }
//////////
// Create an accessor.
//////////
RETURN_ERROR(pRowset->QueryInterface(IID_IAccessor, (void**)&accessor));
RETURN_ERROR(accessor->CreateAccessor(DBACCESSOR_ROWDATA, numColumns, columnBinding, bufferLength, &readAccess, NULL));
// I used this hokey method of assigning the pointer to avoid a
// dependency on atlimpl.cpp
//
// We do this assignment last, so that the presence of a rowset means the
// entire initialization succeeded.
(rowset.p = pRowset)->AddRef();
endOfRowset = false;
return S_OK; }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::Detach
//
// DESCRIPTION
//
// Frees all the resources associated with the current rowset.
//
///////////////////////////////////////////////////////////////////////////////
IRowset* CSimpleTable::Detach() { ReleaseRows();
delete[] buffer; buffer = NULL;
delete[] columnBinding; columnBinding = NULL;
CoTaskMemFree(columnInfo); columnInfo = NULL;
CoTaskMemFree(stringsBuffer); stringsBuffer = NULL;
accessor.Release(); rowsetChange.Release();
IRowset* temp = rowset; rowset.Release(); return temp; }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::MoveFirst
//
// DESCRIPTION
//
// Positions the cursor over the first row in the rowset.
//
///////////////////////////////////////////////////////////////////////////////
HRESULT CSimpleTable::MoveFirst() { if (rowset == NULL) return E_FAIL;
ReleaseRows();
RETURN_ERROR(rowset->RestartPosition(NULL));
endOfRowset = false;
return MoveNext(); }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::MoveNext
//
// DESCRIPTION
//
// Positions the cursor over the next row in the rowset.
//
///////////////////////////////////////////////////////////////////////////////
HRESULT CSimpleTable::MoveNext() { // If the data wasn't opened successfully then fail
if (rowset == NULL) return E_FAIL;
// Too late to save any changes.
DiscardChanges();
// If we've used all the rows from the last fetch, then get some more.
if (++currentRow >= numRows) { ReleaseRows();
// We have to do this check here, since some providers automatically
// reset to the beginning of the rowset.
if (endOfRowset) { return DB_S_ENDOFROWSET; }
HROW* pRow = row; HRESULT hr = rowset->GetNextRows(NULL, 0, FETCH_QUANTUM, &numRows, &pRow);
if (hr == DB_S_ENDOFROWSET) { // Mark that we've reached the end of the rowset.
endOfRowset = true;
// If we didn't get any rows, then we're really at the end.
if (numRows == 0) { return DB_S_ENDOFROWSET; } } else if (FAILED(hr)) { return hr; } }
// Load the data into the buffer.
RETURN_ERROR(rowset->GetData(row[currentRow], readAccess, buffer));
return S_OK; }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::Insert
//
// DESCRIPTION
//
// Inserts the contents of the accessor buffer into the rowset.
//
///////////////////////////////////////////////////////////////////////////////
HRESULT CSimpleTable::Insert() { // Is a rowset attached?
if (!rowset) { return E_FAIL; }
// Does this rowset support changes?
if (!rowsetChange) { return E_NOINTERFACE; }
// Get an accessor for the dirty columns.
HACCESSOR writeAccess; RETURN_ERROR(CreateAccessorForWrite(&writeAccess));
// Release the existing rows to make room for the new one.
ReleaseRows();
HRESULT hr = rowsetChange->InsertRow(NULL, writeAccess, buffer, row);
if (SUCCEEDED(hr)) { // The changes were save successfully, so reset the dirty vector.
DiscardChanges();
// We now have exactly one row in our buffer.
numRows = 1; }
// Release the accessor.
accessor->ReleaseAccessor(writeAccess, NULL);
return hr; }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::Delete
//
// DESCRIPTION
//
// Deletes the current row from the rowset.
//
///////////////////////////////////////////////////////////////////////////////
HRESULT CSimpleTable::Delete() { // Are we positioned over a valid row?
if (!rowset || currentRow >= numRows) { return E_FAIL; }
// Does this rowset support changes?
if (!rowsetChange) { return E_NOINTERFACE; }
DBROWSTATUS rowStatus[1];
return rowsetChange->DeleteRows(NULL, 1, row + currentRow, rowStatus); }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::SetData
//
// DESCRIPTION
//
// Updates the current row with the data in the accessor buffer.
//
///////////////////////////////////////////////////////////////////////////////
HRESULT CSimpleTable::SetData() { // Are we positioned over a valid row?
if (!rowset || currentRow >= numRows) { return E_FAIL; }
// Does this rowset support changes?
if (!rowsetChange) { return E_NOINTERFACE; }
// Get an accessor for the dirty columns.
HACCESSOR writeAccess; RETURN_ERROR(CreateAccessorForWrite(&writeAccess));
HRESULT hr = rowsetChange->SetData(row[currentRow], writeAccess, buffer);
if (SUCCEEDED(hr)) { // The changes were save successfully, so reset the dirty vector.
DiscardChanges(); }
// Release the accessor.
accessor->ReleaseAccessor(writeAccess, NULL);
return hr; }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::GetLength
//
// DESCRIPTION
//
// Returns the length of the current value for a given column.
//
///////////////////////////////////////////////////////////////////////////////
DBLENGTH CSimpleTable::GetLength(DBORDINAL nOrdinal) const { return *(DBLENGTH*)((BYTE*)_GetDataPtr(nOrdinal) + columnInfo[OrdinalToColumn(nOrdinal)].ulColumnSize); }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::GetOrdinal
//
// DESCRIPTION
//
// Returns the ordinal for a given column name.
//
///////////////////////////////////////////////////////////////////////////////
bool CSimpleTable::GetOrdinal(LPCWSTR szColumnName, DBORDINAL* pOrdinal) const { for (DBORDINAL i = 0; i < numColumns; ++i) { if (lstrcmpW(columnInfo[i].pwszName, szColumnName) == 0) { *pOrdinal = columnInfo[i].iOrdinal;
return true; } }
return false; }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::GetStatus
//
// DESCRIPTION
//
// Returns the status code associated with the current value of a column.
//
///////////////////////////////////////////////////////////////////////////////
DBSTATUS CSimpleTable::GetStatus(DBORDINAL nOrdinal) const { return *(DBSTATUS*)((BYTE*)_GetDataPtr(nOrdinal) + columnInfo[OrdinalToColumn(nOrdinal)].ulColumnSize + sizeof(DBLENGTH)); }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::CreateAccessorForWrite
//
// DESCRIPTION
//
// Creates an accessor that is only to bound to columns that have been
// modified.
//
///////////////////////////////////////////////////////////////////////////////
HRESULT CSimpleTable::CreateAccessorForWrite(HACCESSOR* phAccessor) { //////////
// Allocate temporary space for the bindings.
//////////
DBBINDING* writeBind = stack_new(DBBINDING, dirty.count());
//////////
// Load in all the dirty columns.
//////////
size_t total = 0;
for (size_t i = 0; total < dirty.count(); ++i) { if (dirty.test(i)) { // We only want to bind the value.
(writeBind[total++] = columnBinding[i]).dwPart = DBPART_VALUE; } }
//////////
// Create the accessor.
//////////
return accessor->CreateAccessor(DBACCESSOR_ROWDATA, dirty.count(), writeBind, bufferLength, phAccessor, NULL); }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::_GetDataPtr
//
// DESCRIPTION
//
// Non-const version of _GetDataPtr. Marks the target column as dirty.
//
///////////////////////////////////////////////////////////////////////////////
void* CSimpleTable::_GetDataPtr(DBORDINAL nOrdinal) { DBORDINAL nColumn = OrdinalToColumn(nOrdinal);
dirty.set(nColumn);
return buffer + (ULONG_PTR)columnInfo[nColumn].pTypeInfo; }
///////////////////////////////////////////////////////////////////////////////
//
// METHOD
//
// CSimpleTable::ReleaseRows
//
// DESCRIPTION
//
// Releases all the rows returned by the last fetch.
//
///////////////////////////////////////////////////////////////////////////////
HRESULT CSimpleTable::ReleaseRows() { if (rowset != NULL) { HRESULT hr = rowset->ReleaseRows(numRows, row, NULL, NULL, NULL);
currentRow = numRows = 0;
return hr; }
return S_OK; }
|