//+------------------------------------------------------------------------- // // 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 #include #include #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 ColumnInfo( cColumns ); XArrayOLE 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 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 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 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; }