windows-nt/Source/XPSP1/NT/inetsrv/query/bigtable/colinfo.cxx
2020-09-26 16:20:57 +08:00

1352 lines
43 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1994 - 2000.
//
// File: colinfo.cxx
//
// Contents: Column information for rowsets
//
// Classes: CColumnsInfo
//
// Notes: Designed as an aggregated class of an IRowset or an
// IQuery implementation.
//
// History: 04 Feb 1995 AlanW Created
//
//--------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
#include <colinfo.hxx>
#include <query.hxx>
#include <tblvarnt.hxx>
#include "tblrowal.hxx"
#include "tabledbg.hxx"
// Always bind as DBTYPE_VARIANT, so we can use provider-owned memory
#define ALWAYS_USE_VARIANT_BINDING
ULONG CColumnsInfo::_nUnique = 0;
const static GUID guidBmk = DBBMKGUID;
//+-------------------------------------------------------------------------
//
// Method: CColumnsInfo::QueryInterface, public
//
// Synopsis: Invokes QueryInterface on controlling unknown object
//
// Arguments: [riid] -- interface ID
// [ppvObject] -- returned interface pointer
//
// Returns: SCODE
//
// History: 04 Feb 1995 AlanW Created
//
//--------------------------------------------------------------------------
STDMETHODIMP CColumnsInfo::QueryInterface(
REFIID riid,
void **ppvObject)
{
return _rUnknown.QueryInterface(riid,ppvObject);
} //QueryInterface
//+-------------------------------------------------------------------------
//
// Method: CColumnsInfo::AddRef, public
//
// Synopsis: Invokes AddRef on controlling unknown object
//
// Returns: ULONG
//
// History: 04 Feb 1995 AlanW Created
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CColumnsInfo::AddRef()
{
return _rUnknown.AddRef();
} //AddRef
//+-------------------------------------------------------------------------
//
// Method: CColumnsInfo::Release, public
//
// Synopsis: Invokes Release on controlling unknown object
//
// Returns: ULONG
//
// History: 04 Feb 1995 AlanW Created
//
//--------------------------------------------------------------------------
STDMETHODIMP_(ULONG) CColumnsInfo::Release()
{
return _rUnknown.Release();
} //Release
//+---------------------------------------------------------------------------
//
// Member: CColumnsInfo::CColumnsInfo, public
//
// Synopsis: Creates a column information class
//
// Arguments: [cols] -- a reference to the output column set, pidmapped
// [pidmap] -- property IDs and names for the columns
// [ErrorObject] -- a reference to enclosing object's error obj.
// [rUnknown] -- a reference to the controlling IUnknown
// [fSequential] -- TRUE if the query is sequential
//
// Notes:
//
// History: 04 Feb 1995 AlanW Created
//
//----------------------------------------------------------------------------
CColumnsInfo::CColumnsInfo(
CColumnSet const & cols,
CPidMapperWithNames const & pidmap,
CCIOleDBError & ErrorObject,
IUnknown & rUnknown,
BOOL fSequential ) :
_idUnique(0),
_rUnknown(rUnknown),
_fSequential(fSequential),
_fChaptered(FALSE),
_cbRowWidth(0),
_cColumns( cols.Count() ),
_cBoundColumns(0),
_iColRowId(pidInvalid),
_pColumns(0),
_pidmap( cols.Count()+1 ),
_ErrorObject( ErrorObject ),
_fNotPrepared(FALSE)
{
_SetColumns(cols, pidmap, fSequential);
}
//+---------------------------------------------------------------------------
//
// Member: CColumnsInfo::CColumnsInfo, public
//
// Synopsis: Creates an empty column information class
//
// Arguments: [rUnknown] -- a reference to the controlling IUnknown
// [ErrorObject] -- a reference to enclosing object's error obj.
//
// Notes: Used in the command object where column information may
// change, depending upon the command.
//
// History: 11 Aug 1997 AlanW Created
//
//----------------------------------------------------------------------------
CColumnsInfo::CColumnsInfo(
IUnknown & rUnknown,
CCIOleDBError & ErrorObject,
BOOL fNotPrepared) :
_idUnique(_GetNewId()),
_rUnknown(rUnknown),
_fSequential(FALSE),
_fChaptered(FALSE),
_cbRowWidth(0),
_cColumns( 0 ),
_cBoundColumns(0),
_iColRowId(pidInvalid),
_pColumns(0),
_pidmap( 0 ),
_ErrorObject( ErrorObject ),
_fNotPrepared(fNotPrepared)
{
}
//+---------------------------------------------------------------------------
//
// Member: CColumnsInfo::~CColumnsInfo, public
//
// Synopsis: Destroys a column information class
//
// Notes:
//
// History: 12 Feb 1995 AlanW Created
//
//----------------------------------------------------------------------------
CColumnsInfo::~CColumnsInfo( )
{
delete _pColumns;
}
//+---------------------------------------------------------------------------
//
// Member: CColumnsInfo::InitColumns, public
//
// Synopsis: Initializes or reinitializes columns.
//
// Arguments: [cols] -- a reference to the output column set, pidmapped
// [pidmap] -- property IDs and names for the columns
// [fSequential] -- TRUE if the query is sequential
//
// Notes:
//
// History: 11 Aug 1997 AlanW Created
//
//----------------------------------------------------------------------------
void CColumnsInfo::InitColumns (
CColumnSet const & cols,
CPidMapperWithNames const & pidmap,
BOOL fSequential )
{
_pidmap.Clear();
_cColumns = cols.Count();
_fSequential = fSequential;
_SetColumns(cols, pidmap, fSequential);
_fChaptered = FALSE;
_fNotPrepared = FALSE;
}
//+---------------------------------------------------------------------------
//
// Member: CColumnsInfo::InitColumns, public
//
// Synopsis: Reinitializes columns to be null.
//
// Arguments: [fNotPrepared] - TRUE if GetColumnInfo and MapColumnIDs should
// return DB_E_NOTPREPARED for a command object
//
// Notes:
//
// History: 11 Aug 1997 AlanW Created
//
//----------------------------------------------------------------------------
void CColumnsInfo::InitColumns ( BOOL fNotPrepared )
{
_pidmap.Clear();
_cColumns = 0;
_fSequential = FALSE;
_fChaptered = FALSE;
_fNotPrepared = fNotPrepared;
}
//+---------------------------------------------------------------------------
//
// Member: CColumnsInfo::_SetColumns, private
//
// Synopsis: Initializes or reinitializes columns.
//
// Arguments: [cols] -- a reference to the output column set, pidmapped
// [pidmap] -- property IDs and names for the columns
// [fSequential] -- TRUE if the query is sequential
//
// Notes:
//
// History: 11 Aug 1997 AlanW Created
//
//----------------------------------------------------------------------------
void CColumnsInfo::_SetColumns (
CColumnSet const & cols,
CPidMapperWithNames const & pidmap,
BOOL fSequential )
{
Win4Assert( 0 == _pColumns && 0 == _cbRowWidth );
_idUnique = _GetNewId();
//
// We want the PidMapper to give back 1-based column numbers;
// add either a null propspec or the bookmark column as its first element.
//
if (fSequential)
{
CFullPropSpec nullCol;
_pidmap.NameToPid( nullCol );
}
else
{
CFullPropSpec bmkCol( guidBmk, PROPID_DBBMK_BOOKMARK );
_pidmap.NameToPid( bmkCol );
}
for (unsigned i = 0; i < _cColumns; i++)
{
PROPID pidTmp = cols.Get(i);
const CFullPropSpec & ColId = *pidmap.Get(pidTmp);
if (ColId.IsPropertyPropid() &&
ColId.GetPropSet() == guidBmk)
{
Win4Assert( !fSequential );
if (ColId.GetPropertyPropid() == PROPID_DBBMK_BOOKMARK)
{
if (0 != pidmap.GetFriendlyName(pidTmp))
{
_pidmap.SetFriendlyName( 0, pidmap.GetFriendlyName(pidTmp) );
}
continue;
}
else if (ColId.GetPropertyPropid() == PROPID_DBBMK_CHAPTER)
{
_fChaptered = TRUE;
}
}
PROPID pidNew = _pidmap.NameToPid( ColId );
if (0 != pidmap.GetFriendlyName(pidTmp))
{
_pidmap.SetFriendlyName( pidNew, pidmap.GetFriendlyName(pidTmp) );
}
}
// In case of duplicate columns, _cColumns needs to be adjusted.
Win4Assert( _pidmap.Count() > 1 );
_cColumns = _pidmap.Count() - 1;
}
//+---------------------------------------------------------------------------
//
// Member: CColumnsInfo::MapColumnID, private
//
// Synopsis: Map a column identifier to its column number in the
// cursor.
//
// Arguments: [pColumnId] - A pointer to the column identifier.
//
// Returns: The column number (1-based). Returns DB_INVALIDCOLUMN
// on error.
//
// History: 04 Feb 1995 AlanW Created
//
//----------------------------------------------------------------------------
ULONG CColumnsInfo::MapColumnID(
const DBID * pColumnId
) {
PROPID pid = pidInvalid;
if (pColumnId->eKind == DBKIND_PGUID_PROPID ||
pColumnId->eKind == DBKIND_PGUID_NAME)
{
DBID dbcolMapped = *pColumnId;
dbcolMapped.uGuid.guid = *pColumnId->uGuid.pguid;
if (pColumnId->eKind == DBKIND_PGUID_PROPID)
dbcolMapped.eKind = DBKIND_GUID_PROPID;
else
dbcolMapped.eKind = DBKIND_GUID_NAME;
pid = _pidmap.NameToPid(dbcolMapped);
}
else
{
pid = _pidmap.NameToPid(*pColumnId);
}
tbDebugOut(( DEB_ITRACE, "pid: 0x%x, _cColumns: %d\n",
pid, _cColumns ));
if (pid == pidInvalid || pid > _cColumns || (pid == 0 && _fSequential) )
return (ULONG) DB_INVALIDCOLUMN;
return pid;
}
//+---------------------------------------------------------------------------
//
// Member: CColumnsInfo::MapColumnIDs, public
//
// Synopsis: Map a column identifier to its column number in the
// rowset.
//
// Arguments: [cColumnIDs] -- # of elements in the arrays
// [rgColumnIDs] -- A pointer to the column identifiers
// [rgColumns] -- an array in which to return the column numbers.
//
// Returns: SCODE - DB_S_ERRORSOCCURRED, an element of rgColumnIDs was
// invalid
//
// Notes: Column numbers are 1-based.
//
//----------------------------------------------------------------------------
STDMETHODIMP CColumnsInfo::MapColumnIDs(
DBORDINAL cColumnIDs,
const DBID rgColumnIDs[],
DBORDINAL rgColumns[])
{
_ErrorObject.ClearErrorInfo();
SCODE sc = S_OK;
if ((0 != cColumnIDs && 0 == rgColumnIDs) ||
0 == rgColumns)
return _ErrorObject.PostHResult(E_INVALIDARG, IID_IColumnsInfo);
if ( 0 == cColumnIDs )
return S_OK;
if ( _fNotPrepared )
return _ErrorObject.PostHResult(DB_E_NOTPREPARED, IID_IColumnsInfo);
if ( 0 == _cColumns )
return _ErrorObject.PostHResult(DB_E_NOCOMMAND, IID_IColumnsInfo);
unsigned cBadMapping = 0;
TRY
{
for (ULONG i = 0; i < cColumnIDs; i++)
{
ULONG ulColID = MapColumnID( &rgColumnIDs[i] );
rgColumns[i] = ulColID;
if (ulColID == DB_INVALIDCOLUMN)
cBadMapping++;
}
}
CATCH( CException, e )
{
_ErrorObject.PostHResult(e.GetErrorCode(), IID_IColumnsInfo);
sc = GetOleError(e);
}
END_CATCH;
if (SUCCEEDED(sc) && cBadMapping)
sc = (cBadMapping == cColumnIDs) ? DB_E_ERRORSOCCURRED :
DB_S_ERRORSOCCURRED;
return sc;
} //MapColumnIDs
//+---------------------------------------------------------------------------
//
// Member: CColumnsInfo::GetColumnInfo, public
//
// Synopsis: Return information about the columns in the rowset.
//
// Arguments: [pcColumns] - A pointer to where the number of columns
// will be returned.
// [prgInfo] - A pointer to where a pointer to an array of
// DBCOLUMNINFO structures describing the columns
// will be returned. This must be freed by the
// caller.
// [ppStringsBuffer] - A pointer to where extra data for strings
// will be returned. This must be freed by the
// caller if non-null.
//
// Returns: SCODE
//
// Notes: Some columns are standard columns available for all file
// stores. For these columns, full information about data
// type and sizes can be returned. For any other columns,
// we can only say that it has a variant type.
//
// History: 07 Nov 1994 AlanW Created
// 04 Feb 1995 AlanW Moved to CColumnsInfo and rewritten
//
//----------------------------------------------------------------------------
STDMETHODIMP CColumnsInfo::GetColumnInfo(
DBORDINAL * pcColumns,
DBCOLUMNINFO * * prgInfo,
WCHAR * * ppStringsBuffer)
{
_ErrorObject.ClearErrorInfo();
SCODE scResult = S_OK;
//
// Initialize arguments before returning errors
//
if ( pcColumns)
*pcColumns = 0;
if ( prgInfo )
*prgInfo = 0;
if (ppStringsBuffer )
*ppStringsBuffer = 0;
if (0 == pcColumns ||
0 == prgInfo ||
0 == ppStringsBuffer)
return _ErrorObject.PostHResult(E_INVALIDARG, IID_IColumnsInfo);
if ( _fNotPrepared )
return _ErrorObject.PostHResult(DB_E_NOTPREPARED, IID_IColumnsInfo);
if ( 0 == _cColumns )
return _ErrorObject.PostHResult(DB_E_NOCOMMAND, IID_IColumnsInfo);
TRY
{
unsigned iFirstCol = _fSequential ? 1 : 0;
unsigned cColumns = GetColumnCount() + 1 - iFirstCol;
//
// The total size required for the output array depends upon
// the size of variable data discovered in the column information.
// Although we could reallocate the memory we'll be writing into,
// we'll just run through the loop twice, once to compute the
// needed space, and the second time to copy the data out after
// doing our allocation.
//
ULONG cchNames = 0;
for (unsigned iCol = iFirstCol; iCol <= GetColumnCount(); iCol++)
{
const CFullPropSpec & ColId = *_pidmap.Get(iCol);
if (ColId.IsPropertyName())
{
cchNames += wcslen(ColId.GetPropertyName()) + 1;
}
WCHAR const * pwszColName = _pidmap.GetFriendlyName(iCol);
if (0 == pwszColName)
pwszColName = _FindColumnInfo(ColId).pwszName;
if (pwszColName)
{
cchNames += wcslen(pwszColName) + 1;
}
}
XArrayOLE<DBCOLUMNINFO> ColumnInfo( cColumns );
XArrayOLE<WCHAR> StringBuf( cchNames );
DBCOLUMNINFO *pColInfo = ColumnInfo.GetPointer();
WCHAR * pwcNames = StringBuf.GetPointer();
for (iCol = iFirstCol; iCol <= GetColumnCount(); iCol++, pColInfo++)
{
const CFullPropSpec & ColId = *_pidmap.Get(iCol);
const DBCOLUMNINFO & rColumnInfo = _FindColumnInfo( ColId );
//
// Copy the prototype column information, then update
// specific fields in the column info:
// column number
// column ID
// copies of strings
//
*pColInfo = rColumnInfo;
pColInfo->iOrdinal = iCol;
pColInfo->columnid.uGuid.guid = ColId.GetPropSet();
if (ColId.IsPropertyName())
{
pColInfo->columnid.eKind = DBKIND_GUID_NAME;
ULONG cch = wcslen(ColId.GetPropertyName()) + 1;
RtlCopyMemory(pwcNames, ColId.GetPropertyName(),
cch * sizeof (WCHAR));
pColInfo->columnid.uName.pwszName = pwcNames;
pwcNames += cch;
}
else
{
Win4Assert(ColId.IsPropertyPropid());
pColInfo->columnid.eKind = DBKIND_GUID_PROPID;
pColInfo->columnid.uName.ulPropid = ColId.GetPropertyPropid();
}
WCHAR const * pwszColName = _pidmap.GetFriendlyName(iCol);
if (0 == pwszColName)
pwszColName = _FindColumnInfo(ColId).pwszName;
if (pwszColName)
{
ULONG cch = wcslen(pwszColName) + 1;
RtlCopyMemory(pwcNames, pwszColName, cch * sizeof (WCHAR));
pColInfo->pwszName = pwcNames;
pwcNames += cch;
}
}
Win4Assert( (unsigned)(pColInfo - ColumnInfo.GetPointer()) == cColumns );
*prgInfo = ColumnInfo.Acquire();
if (cchNames > 0)
*ppStringsBuffer = StringBuf.Acquire();
*pcColumns = cColumns;
}
CATCH( CException, e )
{
scResult = e.GetErrorCode();
_ErrorObject.PostHResult(scResult, IID_IColumnsInfo);
if (scResult != E_OUTOFMEMORY)
scResult = E_FAIL;
}
END_CATCH;
return scResult;
}
//+---------------------------------------------------------------------------
//
// Member: CColumnsInfo::SetColumnBindings, public
//
// Synopsis: Set current column bindings on the cursor. Save in
// member variables. Workid is always added to the
// bindings for movable rowsets for use with bookmarks
// and hRows. Space for a USHORT reserved for row buffer
// refcounting is always allocated.
//
// Arguments: [rpQuery] - a reference to the PQuery for the query
// [hCursor] - a reference to the hCursor to have column
// bindings set on.
// [obRowRefcount] - on return, offset into the row buffer
// where a USHORT reference count can be stored.
// [obRowId] - on return, offset into the row buffer where
// the row identifier is stored. Not valid for
// sequential rowsets.
//
// Returns: Nothing. Throws on errors.
//
// Notes: Initializes the private members _cbRowWidth and _pColumns.
//
// History: 04 Feb 1995 AlanW Created
//
//----------------------------------------------------------------------------
void CColumnsInfo::SetColumnBindings(
PQuery & rpQuery,
ULONG hCursor,
ULONG &obRowRefcount,
ULONG &obRowId,
ULONG &obChaptRefcount,
ULONG &obChaptId
) {
CTableRowAlloc RowMap( 0 );
USHORT maxAlignment = sizeof (USHORT);
obRowRefcount = RowMap.AllocOffset( sizeof (USHORT),
sizeof (USHORT),
TRUE );
if (_fChaptered)
obChaptRefcount = RowMap.AllocOffset( sizeof (USHORT),
sizeof (USHORT),
TRUE );
else
obChaptRefcount = 0xFFFFFFFF;
obRowId = 0xFFFFFFFF;
obChaptId = 0xFFFFFFFF;
BOOL fAddedWorkId = FALSE;
BOOL fMayDefer = FALSE;
// +1 In case WorkID or Path is added for rowid
XPtr<CTableColumnSet> XColumns( new CTableColumnSet( GetColumnCount() + 1 ));
unsigned cBoundColumns = 0;
tbDebugOut(( DEB_ITRACE, "original column count: %d\n", GetColumnCount() ));
for (unsigned iCol = 1; iCol <= GetColumnCount(); iCol++)
{
const CFullPropSpec & ColId = *_pidmap.Get( iCol );
const DBCOLUMNINFO & rColumnInfo = _FindColumnInfo( ColId );
tbDebugOut(( DEB_ITRACE, "colinfo::set, top of loop, cBoundColumns: %d\n",
cBoundColumns ));
tbDebugOut(( DEB_ITRACE, "adding '%ws'\n", rColumnInfo.pwszName ));
//
// If this is bookmark column, it will be mapped to the row ID
// column. It's only valid for locatable rowsets.
//
if ( (rColumnInfo.dwFlags & DBCOLUMNFLAGS_ISBOOKMARK) &&
ColId.IsPropertyPropid() &&
ColId.GetPropertyPropid() == PROPID_DBBMK_BOOKMARK)
{
tbDebugOut(( DEB_ITRACE, "skipping bookmark column\n" ));
Win4Assert(! _fSequential );
if (_fSequential)
THROW(CException(E_FAIL));
continue;
}
// the self columns is resolved in the accessor -- no binding needed
if ( ( ColId.IsPropertyPropid() ) &&
( ColId.GetPropertyPropid() == PROPID_DBSELF_SELF ) &&
( ColId.GetPropSet() == DBCOL_SELFCOLUMNS ) )
{
continue;
}
//
// Create the new column. Note that its PropID is the
// 1-based column ID.
//
XPtr<CTableColumn> TableCol(new CTableColumn( iCol ));
#ifndef ALWAYS_USE_VARIANT_BINDING
VARTYPE vt = rColumnInfo.wType;
switch (vt)
{
case DBTYPE_VARIANT:
{
#endif // ndef ALWAYS_USE_VARIANT_BINDING
fMayDefer = TRUE;
TableCol->SetValueField( DBTYPE_VARIANT,
RowMap.AllocOffset( sizeof (PROPVARIANT),
sizeof (LONGLONG),
TRUE ),
sizeof (PROPVARIANT));
// The status column is interesting for all columns
TableCol->SetStatusField( RowMap.AllocOffset( sizeof (BYTE),
sizeof (BYTE),
TRUE ),
sizeof (BYTE));
// Length is interesting, especially when the value is deferred
TableCol->SetLengthField( RowMap.AllocOffset( sizeof (ULONG),
sizeof (ULONG),
TRUE ),
sizeof (ULONG));
USHORT cbData, cbAlignment, rgfFlags;
CTableVariant::VartypeInfo(DBTYPE_VARIANT, cbData, cbAlignment, rgfFlags);
if ( cbAlignment > maxAlignment)
maxAlignment = cbAlignment;
#ifndef ALWAYS_USE_VARIANT_BINDING
break;
}
case DBTYPE_DATE:
case DBTYPE_WSTR:
case DBTYPE_STR:
//
// Adjust DBTYPEs from the column info into ones that are
// better for binding.
//
if (vt == DBTYPE_DATE)
vt = VT_FILETIME;
else if (vt == DBTYPE_WSTR)
vt = VT_LPWSTR;
else if (vt == DBTYPE_STR)
vt = VT_LPSTR;
// NOTE: fall through
default:
USHORT cbData, cbAlignment, rgfFlags;
CTableVariant::VartypeInfo(vt, cbData, cbAlignment, rgfFlags);
if (rgfFlags & CTableVariant::MultiSize)
cbData = (USHORT) rColumnInfo.ulColumnSize;
Win4Assert(cbData != 0 || vt == VT_EMPTY);
if (cbData == 0 && vt != VT_EMPTY)
{
tbDebugOut(( DEB_WARN,
"CColumnInfo::SetColumnBindings - Unknown variant type %4x\n",
vt));
}
if (cbAlignment)
{
if (cbAlignment > maxAlignment)
{
maxAlignment = cbAlignment;
}
}
else
{
cbAlignment = 1;
}
if (cbData != 0)
{
TableCol->SetValueField( vt,
RowMap.AllocOffset( cbData,
cbAlignment,
TRUE ),
cbData);
Win4Assert( 0 == ( (TableCol->GetValueOffset()) % cbAlignment ) );
//
// The status column is interesting for almost all columns,
// even inline columns, since a summary catalog might have
// VT_EMPTY data for these columns (eg storage props).
//
TableCol->SetStatusField( RowMap.AllocOffset( sizeof (BYTE),
sizeof (BYTE),
TRUE ),
sizeof (BYTE));
}
}
#endif // ndef ALWAYS_USE_VARIANT_BINDING
//
// If this is the row ID column, save its offset in the row.
//
if (rColumnInfo.dwFlags & DBCOLUMNFLAGS_ISROWID)
{
#ifdef ALWAYS_USE_VARIANT_BINDING
Win4Assert(TableCol->GetStoredType() == VT_VARIANT &&
TableCol->IsValueStored() &&
TableCol->GetValueSize() == sizeof (PROPVARIANT));
PROPVARIANT prop;
obRowId = TableCol->GetValueOffset() +
(DWORD)((BYTE *) &prop.lVal - (BYTE *)&prop);
#else // ndef ALWAYS_USE_VARIANT_BINDING
Win4Assert(TableCol->GetStoredType() == VT_I4 &&
TableCol->IsValueStored() &&
TableCol->GetValueSize() == sizeof (ULONG));
obRowId = TableCol->GetValueOffset();
#endif // ndef ALWAYS_USE_VARIANT_BINDING
_iColRowId = iCol;
fAddedWorkId = TRUE;
}
//
// If this is the chapter column, save its offset in the row.
//
if (rColumnInfo.dwFlags & DBCOLUMNFLAGS_ISCHAPTER)
{
Win4Assert( _fChaptered );
#ifdef ALWAYS_USE_VARIANT_BINDING
Win4Assert(TableCol->GetStoredType() == VT_VARIANT &&
TableCol->IsValueStored() &&
TableCol->GetValueSize() == sizeof (PROPVARIANT));
PROPVARIANT prop;
obChaptId = TableCol->GetValueOffset() +
(DWORD)((BYTE *) &prop.lVal - (BYTE *)&prop);
#else // ndef ALWAYS_USE_VARIANT_BINDING
Win4Assert(TableCol->GetStoredType() == VT_I4 &&
TableCol->IsValueStored() &&
TableCol->GetValueSize() == sizeof (ULONG));
obChaptId = TableCol->GetValueOffset();
#endif // ndef ALWAYS_USE_VARIANT_BINDING
}
XColumns->Add(TableCol.GetPointer(), cBoundColumns++);
TableCol.Acquire();
}
tbDebugOut(( DEB_ITRACE, "colinfo::set, after loop, cBoundColumns: %d\n",
cBoundColumns ));
// Need to add workid for non-sequential queries so that bookmarks
// work, and either workid or path so that deferred values work.
if ( ( ( !_fSequential ) ||
( fMayDefer && rpQuery.CanDoWorkIdToPath() ) ) &&
( !fAddedWorkId ) )
{
tbDebugOut(( DEB_ITRACE, "colinfo::set, adding WID column\n" ));
//
// Need to add the row ID column to the bindings, so that bookmarks
// work, and deferred values can be loaded.
//
const DBCOLUMNINFO & rColumnInfo = _GetRowIdColumnInfo( );
unsigned iCol = _pidmap.NameToPid( rColumnInfo.columnid );
XPtr<CTableColumn> TableCol(new CTableColumn( iCol ));
_iColRowId = iCol;
Win4Assert (VT_I4 == rColumnInfo.wType);
if (sizeof (ULONG) > maxAlignment)
maxAlignment = sizeof (ULONG);
TableCol->SetValueField( VT_I4,
RowMap.AllocOffset( sizeof (ULONG),
sizeof (ULONG),
TRUE ),
sizeof (ULONG));
obRowId = TableCol->GetValueOffset();
TableCol->SetStatusField( RowMap.AllocOffset( sizeof (BYTE),
sizeof (BYTE),
TRUE ),
sizeof (BYTE));
XColumns->Add(TableCol.GetPointer(), cBoundColumns++);
TableCol.Acquire();
}
ULONG rem = RowMap.GetRowWidth() % maxAlignment;
if ( 0 == rem )
_cbRowWidth = RowMap.GetRowWidth();
else
_cbRowWidth = RowMap.GetRowWidth() + maxAlignment - rem;
rpQuery.SetBindings( hCursor,
_cbRowWidth,
XColumns.GetReference(),
_pidmap );
tbDebugOut(( DEB_ITRACE, "colinfo::set, old # cols %d, new # cols %d\n",
_cColumns, cBoundColumns ));
_cBoundColumns = cBoundColumns;
_pColumns = XColumns.Acquire();
}
//+---------------------------------------------------------------------------
//
// Member: CColumnsInfo::Get1ColumnInfo, public
//
// Synopsis: Return information about a single column in the rowset.
//
// Arguments: [iColumn] - the column number whose column info is to be
// returned.
//
// Returns: DBCOLUMNINFO & - a pointer to column info for the column
//
// Notes:
//
// History: 29 Mar 1995 AlanW Created
//
//----------------------------------------------------------------------------
const DBCOLUMNINFO & CColumnsInfo::Get1ColumnInfo(
ULONG iColumn
) /*const*/
{
const CFullPropSpec & ColId = *_pidmap.Get(iColumn);
return _FindColumnInfo( ColId );
}
#ifdef INCLUDE_COLUMNS_ROWSET_IMPLEMENTATION
STDMETHODIMP CRowset::GetColumnsRowset(
ULONG cSelections,
DBID rgColumnSelection[],
IRowset ** ppColCursor
) /*const*/ {
_ErrorObject.ClearErrorInfo();
return _ErrorObject.PostHResult(E_NOTIMPL, IID_IColumnsRowset);
}
STDMETHODIMP CRowset::GetAvailableColumns(
ULONG * pcSelections,
DBID ** rgColumnSelection
) /*const*/ {
_ErrorObject.ClearErrorInfo();
return _ErrorObject.PostHResult(E_NOTIMPL, IID_IColumnsRowset);
}
#endif // INCLUDE_COLUMNS_ROWSET_IMPLEMENTATION
////////////////////////////////////////////////////////////
//
// Data declarations for _FindColumnInfo
//
// Notes: These arrays of structures are prototype column info.
// structures returned by _FindColumnInfo.
//
////////////////////////////////////////////////////////////
static const DBCOLUMNINFO aStoragePropDescs[] = {
{ L"FileDirectoryName", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE, MAX_PATH, // storage props are never deferred
DBTYPE_WSTR, 0xff, 0xff,
{ PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PID_STG_DIRECTORY ) }
},
{ L"ClassID", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (GUID),
DBTYPE_GUID, 0xff, 0xff,
{ PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PID_STG_CLASSID ) }
},
{ L"FileStorageType", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (ULONG),
DBTYPE_UI4, 0xff, 0xff,
{ PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PID_STG_STORAGETYPE ) }
},
{ L"FileIndex", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONGLONG),
DBTYPE_I8, 0xff, 0xff,
{ PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PID_STG_FILEINDEX ) }
},
{ L"FileUSN", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONGLONG),
DBTYPE_I8, 0xff, 0xff,
{ PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PID_STG_LASTCHANGEUSN ) }
},
{ L"FileName", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE, MAX_PATH, // storage props are never deferred
DBTYPE_WSTR, 0xff, 0xff,
{ PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PID_STG_NAME ) }
},
{ L"FilePathName", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE, MAX_PATH, // storage props are never deferred
DBTYPE_WSTR, 0xff, 0xff,
{ PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PID_STG_PATH ) }
},
{ L"FileSize", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONGLONG),
DBTYPE_I8, 0xff, 0xff,
{ PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PID_STG_SIZE ) }
},
{ L"FileAttributes", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (ULONG),
DBTYPE_UI4, 0xff, 0xff,
{ PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PID_STG_ATTRIBUTES ) }
},
// NOTE: file times are typed as DBTYPE_DATE, but are bound to the
// table as VT_FILETIME.
{ L"FileWriteTime", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONGLONG),
DBTYPE_DATE, 0xff, 0xff,
{ PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PID_STG_WRITETIME ) }
},
{ L"FileCreateTime", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONGLONG),
DBTYPE_DATE, 0xff, 0xff,
{ PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PID_STG_CREATETIME ) }
},
{ L"FileAccessTime", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONGLONG),
DBTYPE_DATE, 0xff, 0xff,
{ PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PID_STG_ACCESSTIME ) }
},
{ L"FileShortName", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE, 13, // storage props are never deferred
DBTYPE_WSTR, 0xff, 0xff,
{ PSGUID_STORAGE, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PID_STG_SHORTNAME ) }
},
};
const ULONG cStoragePropDescs =
sizeof aStoragePropDescs /
sizeof aStoragePropDescs[0];
//
// Standard query properties.
// Does not include pidAll or pidContent, those are used only in restrictions.
//
static const DBCOLUMNINFO aQueryPropDescs[] = {
{ L"QueryRankvector", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof(PROPVARIANT),
DBTYPE_VARIANT, 0xff, 0xff,
{ DBQUERYGUID, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( DISPID_QUERY_RANKVECTOR ) }
},
{ L"QueryRank", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONG),
DBTYPE_I4, 0xff, 0xff,
{ DBQUERYGUID, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( DISPID_QUERY_RANK ) }
},
{ L"QueryHitCount", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONG),
DBTYPE_I4, 0xff, 0xff,
{ DBQUERYGUID, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( DISPID_QUERY_HITCOUNT ) }
},
{ L"WorkID", 0, 0,
DBCOLUMNFLAGS_ISFIXEDLENGTH|DBCOLUMNFLAGS_ISROWID, sizeof (LONG),
DBTYPE_I4, 0xff, 0xff,
{ DBQUERYGUID, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( DISPID_QUERY_WORKID ) }
},
{ L"QueryUnfiltered", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof(BOOL),
DBTYPE_BOOL, 0xff, 0xff,
{ DBQUERYGUID, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( DISPID_QUERY_UNFILTERED ) }
},
{ L"QueryVirtualPath", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE, MAX_PATH,
DBTYPE_WSTR, 0xff, 0xff,
{ DBQUERYGUID, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( DISPID_QUERY_VIRTUALPATH ) }
},
#if defined( DISPID_QUERY_NLIRRANK )
{ L"NLIRRank", 0, 0,
DBCOLUMNFLAGS_ISNULLABLE|DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONG),
DBTYPE_I4, 0xff, 0xff,
{ DBQUERYGUID, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( DISPID_QUERY_NLIRRANK ) }
},
#endif // defined( DISPID_QUERY_NLIRRANK )
};
const ULONG cQueryPropDescs =
sizeof aQueryPropDescs /
sizeof aQueryPropDescs[0];
static DBCOLUMNINFO const aBmkPropDescs[] = {
{ L"Bookmark", 0, 0,
DBCOLUMNFLAGS_ISBOOKMARK|DBCOLUMNFLAGS_ISFIXEDLENGTH,
sizeof (CI_TBL_BMK),
DBTYPE_BYTES, 0xff, 0xff,
{ DBBMKGUID, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PROPID_DBBMK_BOOKMARK ) }
},
{ L"Chapter", 0, 0,
DBCOLUMNFLAGS_ISCHAPTER|DBCOLUMNFLAGS_ISBOOKMARK|DBCOLUMNFLAGS_ISFIXEDLENGTH,
sizeof (CI_TBL_CHAPT),
DBTYPE_BYTES, 0xff, 0xff,
{ DBBMKGUID, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PROPID_DBBMK_CHAPTER ) }
},
};
const ULONG cBmkPropDescs =
sizeof aBmkPropDescs /
sizeof aBmkPropDescs[0];
// CLEANCODE: ole-db spec bug #1271 - const GUID init. less than useful
#ifndef DBSELFGUID
#define DBSELFGUID {0xc8b52231,0x5cf3,0x11ce,{0xad,0xe5,0x00,0xaa,0x00,0x44,0x77,0x3d}}
#endif // ndef DBSELFGUID
static DBCOLUMNINFO const aSelfPropDescs[] = {
{ L"Self", 0, 0,
DBCOLUMNFLAGS_ISFIXEDLENGTH,
sizeof( int ),
DBTYPE_I4, 0xff, 0xff,
{ DBSELFGUID, DBKIND_GUID_PROPID, (LPWSTR) UIntToPtr( PROPID_DBSELF_SELF ) }
},
};
const ULONG cSelfPropDescs =
sizeof aSelfPropDescs /
sizeof aSelfPropDescs[0];
#ifdef INCLUDE_COLUMNS_ROWSET_IMPLEMENTATION
static const DBCOLUMNINFO aColInfoPropDescs[] = {
{ L"ColumnId", 0, 0,
DBCOLUMNFLAGS_ISFIXEDLENGTH, 3 * sizeof(PROPVARIANT),
DBTYPE_VARIANT|DBTYPE_VECTOR, 0xff, 0xff,
{ DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)1 }
},
{ L"ColumnName", 0, 0,
0, 20,
DBTYPE_WSTR, 0xff, 0xff,
{ DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)2 }
},
{ L"ColumnNumber", 0, 0,
DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONG),
DBTYPE_I4, 0xff, 0xff,
{ DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)3 }
},
{ L"ColumnType", 0, 0,
DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof(USHORT),
DBTYPE_I2, 0xff, 0xff,
{ DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)4 }
},
{ L"ColumnLength", 0, 0,
DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONG),
DBTYPE_I4, 0xff, 0xff,
{ DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)5 }
},
{ L"ColumnPrecision", 0, 0,
DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONG),
DBTYPE_I4, 0xff, 0xff,
{ DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)6 }
},
{ L"ColumnScale", 0, 0,
DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONG),
DBTYPE_I4, 0xff, 0xff,
{ DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)7 }
},
{ L"ColumnFlags", 0, 0,
DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (LONG),
DBTYPE_I4, 0xff, 0xff,
{ DBCIDGUID, DBKIND_GUID_PROPID, (LPWSTR)8 }
},
};
const ULONG cColInfoPropDescs =
sizeof aColInfoPropDescs /
sizeof aColInfoPropDescs[0];
#endif // INCLUDE_COLUMNS_ROWSET_IMPLEMENTATION
//
// Array of column descriptions per known propset
// Each referenced array must have the same Guid for each element in
// the array.
//
const CColumnsInfo::SPropSetInfo CColumnsInfo::aPropSets [ ] = {
#define IPROPSET_STORAGE 0 // Storage property set index
{ cStoragePropDescs, aStoragePropDescs },
{ cQueryPropDescs, aQueryPropDescs },
{ cBmkPropDescs, aBmkPropDescs },
{ cSelfPropDescs, aSelfPropDescs },
#ifdef INCLUDE_COLUMNS_ROWSET_IMPLEMENTATION
{ cColInfoPropDescs, aColInfoPropDescs },
#endif // INCLUDE_COLUMNS_ROWSET_IMPLEMENTATION
};
const ULONG CColumnsInfo::cPropSets =
sizeof CColumnsInfo::aPropSets /
sizeof CColumnsInfo::aPropSets[0];
DBCOLUMNINFO const DefaultColumnInfo = {
0, 0, 0,
DBCOLUMNFLAGS_ISNULLABLE | DBCOLUMNFLAGS_MAYDEFER | DBCOLUMNFLAGS_ISFIXEDLENGTH, sizeof (PROPVARIANT),
DBTYPE_VARIANT, 0xff, 0xff,
{ {0,0,0,{0,0,0,0,0,0,0,0}}, DBKIND_GUID_PROPID, (LPWSTR)0 }
};
//+---------------------------------------------------------------------------
//
// Member: CColumnsInfo::_FindColumnInfo, static
//
// Synopsis: Return information about a particular column ID.
//
// Arguments: [ColId] - Column ID to be looked up.
//
// Returns: DBCOLUMNINFO - the column information for the row looked up.
//
// Notes: Some columns are standard columns available for all file
// stores. For these columns, full information about data
// type and sizes can be returned. For any other columns,
// a generic column information structure is returned.
//
// History: 10 Feb 1995 AlanW Created
//
//----------------------------------------------------------------------------
DBCOLUMNINFO const & CColumnsInfo::_FindColumnInfo(
const CFullPropSpec & ColId
) {
DBCOLUMNINFO const * pColInfo = &DefaultColumnInfo;
//
// All custom information we return has propids, not prop names.
// Valid property IDs start at 2
//
if (ColId.IsPropertyPropid())
{
for (unsigned iPropSet = 0; iPropSet < cPropSets; iPropSet++)
{
if (ColId.GetPropSet() ==
aPropSets[iPropSet].aPropDescs[0].columnid.uGuid.guid)
{
//
// Found the guid for the propset, now try to find the
// propid.
//
ULONG ulPropId = ColId.GetPropertyPropid();
Win4Assert( ulPropId != PID_CODEPAGE &&
ulPropId != PID_DICTIONARY);
for (unsigned iDesc = 0;
iDesc < aPropSets[iPropSet].cProps;
iDesc++)
{
if (ulPropId ==
aPropSets[iPropSet].aPropDescs[iDesc].columnid.uName.ulPropid)
{
pColInfo = &aPropSets[iPropSet].aPropDescs[iDesc];
break;
}
}
break;
}
}
}
return *pColInfo;
}
//+---------------------------------------------------------------------------
//
// Member: CColumnsInfo::_GetRowIdColumnInfo, static
//
// Synopsis: Return information about the row ID column
//
// Arguments: - None -
//
// Returns: DBCOLUMNINFO - the column information for the row looked up.
//
// Notes: It is assumed that there is only one row ID column in the
// standard column info. This may need to change for chaptered
// rowsets.
//
// History: 15 Mar 1995 AlanW Created
//
//----------------------------------------------------------------------------
DBCOLUMNINFO const & CColumnsInfo::_GetRowIdColumnInfo(
) {
DBCOLUMNINFO const * pColInfo = 0;
for (unsigned iPropSet = 0;
iPropSet < cPropSets && pColInfo == 0;
iPropSet++)
{
for (unsigned iDesc = 0;
iDesc < aPropSets[iPropSet].cProps;
iDesc++)
{
if ( aPropSets[iPropSet].aPropDescs[iDesc].dwFlags &
DBCOLUMNFLAGS_ISROWID)
{
pColInfo = &aPropSets[iPropSet].aPropDescs[iDesc];
break;
}
}
}
Win4Assert(pColInfo != 0);
return *pColInfo;
}