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

844 lines
26 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1992 - 2000.
//
// File: TableCol.cxx
//
// Contents: Large table column descriptors
//
// Classes: CTableColumn
// CTableColumnArray
// CTableColumnSet
//
// History: 15 Sep 94 AlanW Split from coldesc.cxx
//
//--------------------------------------------------------------------------
#include "pch.cxx"
#pragma hdrstop
#include <tablecol.hxx>
#include <tblvarnt.hxx>
#include "colcompr.hxx"
#include "tabledbg.hxx"
#include "pathstor.hxx"
IMPL_DYNARRAY( CTableColumnArray, CTableColumn );
//+-------------------------------------------------------------------------
//
// Member: CTableColumn::~CTableColumn, public
//
// Synopsis: Destructor for a table column description.
//
// Notes:
//
//--------------------------------------------------------------------------
CTableColumn::~CTableColumn( )
{
if (_pCompression && !_fGlobalCompr && _CompressMasterID == 0)
delete _pCompression;
}
//+-------------------------------------------------------------------------
//
// Member: CTableColumnSet::Add, public
//
// Synopsis: Add a column description to a column set.
//
// Arguments: [pCol] - a smart pointer to column to be added.
// [iCol] - position in array to use.
//
// Returns: Nothing
//
// Notes: Care is taken to assure that the smart pointer is
// transferred to the column array without generating
// an exception while holding the bare pointer.
//
//--------------------------------------------------------------------------
void
CTableColumnSet::Add(
XPtr<CTableColumn> & pCol,
unsigned iCol
) {
if (iCol >= Size())
CTableColumnArray::Add(0, iCol); // probe to expand array
CTableColumnArray::Add(pCol.Acquire(), iCol);
if (iCol >= Count())
{
Win4Assert(iCol == Count());
SetCount(iCol+1);
}
}
// CLEANCODE - old version, delete me someday
void
CTableColumnSet::Add(
CTableColumn* pCol,
unsigned iCol
) {
CTableColumnArray::Add(pCol, iCol);
if (iCol >= Count())
{
Win4Assert(iCol == Count());
SetCount(iCol+1);
}
}
//+-------------------------------------------------------------------------
//
// Member: CTableColumnSet::Find, public
//
// Synopsis: Find a particular column in a column array
//
// Arguments: [PropId] - property ID to search for
// [rfFound] - reference to flag, set to TRUE if match found
//
// Returns: CTableColumn* - pointer to the column found, or zero
//
// Notes:
//
//--------------------------------------------------------------------------
CTableColumn *
CTableColumnSet::Find(
PROPID const PropId,
BOOL& rfFound
) const {
for (unsigned i=0; i<Count(); i++) {
CTableColumn* pCol = Get(i);
if (pCol && pCol->PropId == PropId) {
rfFound = TRUE;
return pCol;
}
}
rfFound = FALSE;
return 0;
}
//+-------------------------------------------------------------------------
//
// Member: CTableColumnSet::Marshall, public
//
// Synopsis: Serialize a table column set
//
// Arguments: [stm] - serialization stream
// [pids] - CPidMapper to convert propids for propspecs
//
// Returns: Nothing
//
//--------------------------------------------------------------------------
void CTableColumnSet::Marshall(
PSerStream & stm,
CPidMapper & pids
) const {
stm.PutULong(Count());
for (unsigned i = 0; i < Count(); i++)
Get(i)->Marshall( stm, pids );
}
//+-------------------------------------------------------------------------
//
// Member: CTableColumn::Marshall, public
//
// Synopsis: Serialize a table column
//
// Arguments: [stm] - serialization stream
// [pids] - CPidMapper to convert propids for propspecs
//
// Returns: Nothing
//
//--------------------------------------------------------------------------
void CTableColumn::Marshall(
PSerStream & stm,
CPidMapper & pids
) const {
pids.PidToName(PropId)->Marshall( stm );
stm.PutULong(GetStoredType());
if ( 0 == IsValueStored() )
stm.PutByte( FALSE );
else
{
stm.PutByte( TRUE );
stm.PutUShort( GetValueOffset() );
stm.PutUShort( GetValueSize() );
}
if ( 0 == IsStatusStored() )
stm.PutByte( FALSE );
else
{
stm.PutByte( TRUE );
stm.PutUShort( GetStatusOffset() );
// stm.PutUShort( GetStatusSize() );
}
if ( 0 == IsLengthStored() )
stm.PutByte( FALSE );
else
{
stm.PutByte( TRUE );
stm.PutUShort( GetLengthOffset() );
// stm.PutUShort( GetLengthSize() );
}
}
//+-------------------------------------------------------------------------
//
// Member: CTableColumnSet::CTableColumnSet, public
//
// Synopsis: Construct a table column set from a serialized stream
//
// Arguments: [stm] - serialization stream
// [pids] - CPidMapper to convert propspecs to fake pids
//
// Returns: Nothing
//
//--------------------------------------------------------------------------
CTableColumnSet::CTableColumnSet( PDeSerStream & stm, CPidMapper & pidmap )
: _cColumns(0),
CTableColumnArray( 0 )
{
ULONG cItems = stm.GetULong();
// Guard against attack
if ( cItems > 1000 )
THROW( CException( E_INVALIDARG ) );
SetExactSize( cItems );
for ( unsigned i = 0; i < cItems; i++ )
{
CFullPropSpec prop( stm );
if ( !prop.IsValid() )
THROW( CException( E_OUTOFMEMORY ) );
PROPID pid = pidmap.NameToPid( prop );
XPtr<CTableColumn> Col( new CTableColumn( pid ) );
DBTYPE DataType = (USHORT) stm.GetULong();
//
// Only support VT_VARIANT and VT_I4 bindings. Other ones aren't
// tested or officially supported -- our OLE DB client only uses
// these two. Other datatypes are probably a hacker.
//
if ( VT_I4 == DataType )
{
// VT_I4 must be workid
if ( ! ( ( prop.IsPropertyPropid() ) &&
( prop.GetPropertyPropid() == PROPID_QUERY_WORKID ) &&
( prop.GetPropSet() == PSGUID_QUERY ) ) )
THROW( CException( E_INVALIDARG ) );
}
else if ( VT_VARIANT != DataType )
THROW( CException( E_INVALIDARG ) );
if ( TRUE == stm.GetByte() )
{
USHORT usOffset = stm.GetUShort();
// Check for a bogus offset
if ( usOffset > 0x1000 )
THROW( CException( E_INVALIDARG ) );
// validate the offset
Col->SetValueField ( DataType, usOffset, stm.GetUShort() );
}
if ( TRUE == stm.GetByte() )
{
USHORT usOffset = stm.GetUShort();
// Check for a bogus offset
if ( usOffset > 0x1000 )
THROW( CException( E_INVALIDARG ) );
Col->SetStatusField ( usOffset, sizeof (BYTE) );
}
if ( TRUE == stm.GetByte() )
{
USHORT usOffset = stm.GetUShort();
// Check for a bogus offset
if ( usOffset > 0x1000 )
THROW( CException( E_INVALIDARG ) );
Col->SetLengthField ( usOffset, sizeof (ULONG) );
}
Add( Col.Acquire(), i);
}
}
//+---------------------------------------------------------------------------
//
// Function: _IsSpecialPathProcessing
//
// Synopsis: Tests if the column is a candidate for doing special path
// processing or not.
//
// Arguments: [dstColumn] - The column into which the data must be copied
// in the destination row.
//
// Returns: TRUE if special path processing can be done.
// FALSE o/w
//
// History: 5-23-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
inline
BOOL
CTableColumn::_IsSpecialPathProcessing( CTableColumn const & dstColumn ) const
{
VARTYPE vtDst = dstColumn.GetStoredType();
return ( pidPath == PropId || pidName == PropId ) &&
( vtDst == VT_LPWSTR ) &&
( _StoreAsType == VT_LPWSTR ) &&
( _pCompression != 0);
}
//+---------------------------------------------------------------------------
//
// Function: _GetPathOrFile
//
// Synopsis: Fetches the path or file (if possible) using special processing
// in the path store (if possible).
//
// Arguments: [dstColumn] - Destination column into which the data is
// being copied.
// [pbSrc] - The source window row.
// [rDstPool] - Var allocator for destination variable memory
// allocation.
// [pbDstRow] - Pointer to the destination row.
//
// Returns: TRUE if the pidName/pidPath could be fetched. FALSE o/w.
//
// History: 5-23-95 srikants Created
//
// Notes:
//
//----------------------------------------------------------------------------
BOOL CTableColumn::_GetPathOrFile( CTableColumn const & dstColumn,
const BYTE * pbSrc,
PVarAllocator & rDstPool,
BYTE * pbDstRow
)
{
const BYTE * pbSrcOrg = pbSrc;
CPathStore * pSrcPathStore = 0;
if ( 0 != _pCompression )
{
pSrcPathStore = _pCompression->GetPathStore();
}
if ( 0 == pSrcPathStore )
return FALSE;
// tbDebugOut(( DEB_ITRACE, "Special Path Processing\n" ));
//
// We have a path store.
//
Win4Assert( GetValueSize() == sizeof(PATHID) );
pbSrc += GetValueOffset();
PATHID srcPathId;
RtlCopyMemory( &srcPathId, pbSrc, sizeof(PATHID) );
BOOL fDone = FALSE;
if ( !dstColumn.IsCompressedCol() )
{
//
// Extract the path/file out of the path store for this pathid.
//
WCHAR * pwszDest = pSrcPathStore->Get( srcPathId, PropId, rDstPool );
if ( 0 != pwszDest )
{
ULONG_PTR offset = rDstPool.PointerToOffset( pwszDest );
Win4Assert( dstColumn.GetValueSize() == sizeof(ULONG) );
RtlCopyMemory( pbDstRow + dstColumn.GetValueOffset(),
&offset,
dstColumn.GetValueSize() );
Win4Assert( dstColumn.IsStatusStored() );
dstColumn.SetStatus( pbDstRow, GetStatus( pbSrcOrg ) );
if ( dstColumn.IsLengthStored() )
{
ULONG *pulLength = (ULONG *)
(pbDstRow + dstColumn.GetLengthOffset());
*pulLength = (ULONG) (wcslen( pwszDest ) + 1 ) * sizeof(WCHAR);
}
fDone = TRUE;
}
}
else
{
//
// The destination column is compressed. See if it
// also has a path store and copy to it.
//
Win4Assert( dstColumn.IsValueStored() );
//
// Copy the data to the target.
//
if ( 0 == dstColumn.GetCompressMasterId() )
{
Win4Assert( dstColumn.IsStatusStored() );
dstColumn.SetStatus( pbDstRow, GetStatus( pbSrcOrg ) );
CPathStore * pDstPathStore =
dstColumn.GetCompressor()->GetPathStore();
ULONG* pulRowColDataBuf = dstColumn.GetValueSize() ?
(ULONG*) (pbDstRow + dstColumn.GetValueOffset()) :
0;
Win4Assert( 0 != pulRowColDataBuf );
if ( 0 != pDstPathStore )
{
*pulRowColDataBuf = pDstPathStore->AddPath( *pSrcPathStore, srcPathId );
fDone = TRUE;
}
}
else
{
//
// This is shared compression. There is nothing to store for this
// column.
//
fDone = TRUE;
}
}
return fDone;
}
//+-------------------------------------------------------------------------
//
// Member: CTableColumn::CopyColumnData, public
//
// Synopsis: Copy a column's data fields between two row buffers.
// Coercion of the data can occur for data transfer to
// the user.
//
// Arguments: [pbDstRow] -- destination row buffer
// [rDstColumn] -- destination column description
// [rDstPool] -- destination variable data allocator
// [pbSrcRow] -- source row buffer
// [rSrcPool] -- source variable data allocator
//
// Returns: DBSTATUS -- status of the column copy
//
// History: 22 Feb 1995 AlanW Created
//
//--------------------------------------------------------------------------
DBSTATUS CTableColumn::CopyColumnData(
BYTE * pbDstRow,
CTableColumn const & rDstColumn,
PVarAllocator & rDstPool,
BYTE * pbSrcRow,
PVarAllocator & rSrcPool
)
{
DBSTATUS DstStatus = DBSTATUS_S_OK;
// pull out the data types for clarity below
VARTYPE vtDst = rDstColumn.GetStoredType();
VARTYPE vtSrc = GetStoredType();
#if 0
if (rDstColumn.IsValueStored())
#endif
{
Win4Assert( IsValueStored() );
// the row buffer promises NOT to set the byref bit for these.
// we'll break elsewhere if this is not true.
Win4Assert(vtSrc != ( VT_LPWSTR | VT_BYREF ) );
Win4Assert(vtSrc != ( VT_LPSTR | VT_BYREF ) );
Win4Assert( 0 == ( VT_VECTOR & vtDst ) );
// Transfer a data value
if ( ( vtDst == vtSrc ) &&
( CTableVariant::IsSimpleType( vtDst ) ) &&
! IsCompressedCol() &&
! rDstColumn.IsCompressedCol() )
{
// Data columns equivalent, and not indirect. Just
// copy from source to dest.
Win4Assert( rDstColumn.IsStatusStored() );
rDstColumn.SetStatus( pbDstRow, GetStatus( pbSrcRow ) );
#if CIDBG == 1 || DBG == 1
USHORT cbData, cbAlignment, rgfFlags;
CTableVariant::VartypeInfo(vtSrc, cbData, cbAlignment, rgfFlags);
Win4Assert( CTableVariant::TableIsStoredInline( rgfFlags,
vtSrc ) );
Win4Assert( rDstColumn.GetValueSize() == GetValueSize() &&
cbData == GetValueSize());
#endif // CIDBG == 1 || DBG == 1
RtlCopyMemory( pbDstRow + rDstColumn.GetValueOffset(),
pbSrcRow + GetValueOffset(),
GetValueSize());
// performance: we never bind to length for these fixed-length
// fields.
Win4Assert( !rDstColumn.IsLengthStored() );
//if (rDstColumn.IsLengthStored())
// rDstColumn.SetLength( pbDstRow, rDstColumn.GetValueSize() );
}
else
{
//
// Check if this is "pidPath"/ "pidName" and if the destination
// allows special path processing.
//
if ( !_IsSpecialPathProcessing(rDstColumn) ||
!_GetPathOrFile( rDstColumn, pbSrcRow,
rDstPool, pbDstRow ) )
{
if ( ( vtDst == VT_LPWSTR
|| vtDst == ( DBTYPE_WSTR | DBTYPE_BYREF )
)
&& vtSrc == VT_LPWSTR
&& !IsCompressedCol()
&& !rDstColumn.IsCompressedCol() )
{
//
// Optimized path for the common case of wide string -> wide string
// copy, such as paths and filenames
//
BYTE *pbSrc = pbSrcRow + GetValueOffset();
WCHAR *pszValue = *(WCHAR **) pbSrc;
BYTE *pbDestBuf = pbDstRow + rDstColumn.GetValueOffset();
Win4Assert( (ULONG_PTR) pbDestBuf % 4 == 0 );
if ( pszValue )
{
ULONG cbSrc = ( wcslen(pszValue) + 1 ) * sizeof( WCHAR );
BYTE *pbDest = (BYTE *) rDstPool.CopyTo( cbSrc, (BYTE *) pszValue );
*(BYTE **) pbDestBuf = (BYTE *) rDstPool.PointerToOffset( pbDest );
}
else
{
*(ULONG *) pbDestBuf = 0;
}
Win4Assert( !rDstColumn.IsLengthStored() );
#if 0
if ( rDstColumn.IsLengthStored() )
{
ULONG *pulLength = (ULONG *) pbDstRow + rDstColumn.GetLengthOffset();
*pulLength = GetValueSize();
}
#endif // 0
}
else
{
//
// For any other case, convert to a CTableVariant, then
// copy it using the CopyOrCoerce method. This allows
// coercions and variant to nonvariant, etc. conversions
// to occur.
//
// NOTE: We may want to optimize the PROPVARIANT to PROPVARIANT
// case when offsets are used in both source and
// destination, since this will occur commonly in
// table splits and row fetches.
//
CTableVariant varnt;
XCompressFreeVariant xpVarnt;
if ( CreateVariant(varnt, pbSrcRow, rSrcPool) )
xpVarnt.Set(GetCompressor(), &varnt);
if ( rDstColumn.IsCompressedCol() )
{
Win4Assert( rDstColumn.IsValueStored() &&
! rDstColumn.IsLengthStored() );
//
// Copy the data to the target.
//
if (0 == rDstColumn.GetCompressMasterId())
{
ULONG* pulRowColDataBuf = rDstColumn.GetValueSize() ?
(ULONG*) (pbDstRow + rDstColumn.GetValueOffset()) :
0;
GetValueResult eGvr;
rDstColumn.GetCompressor()->AddData( &varnt,
pulRowColDataBuf,
eGvr);
Win4Assert( eGvr == GVRSuccess );
}
}
else
{
DBLENGTH ulTemp;
DstStatus = varnt.CopyOrCoerce(
pbDstRow + rDstColumn.GetValueOffset(),
rDstColumn.GetValueSize(),
vtDst,
ulTemp,
rDstPool);
}
if (rDstColumn.IsLengthStored())
{
ULONG *pulLength = (ULONG *)
(pbDstRow + rDstColumn.GetLengthOffset());
if (rDstColumn.GetStoredType() == VT_VARIANT)
{
USHORT flags,cbWidth,cbAlign;
CTableVariant::VartypeInfo( varnt.vt,
cbWidth,
cbAlign,
flags );
if ( CTableVariant::TableIsStoredInline( flags,
varnt.vt ) )
*pulLength = (ULONG) cbWidth;
else
*pulLength = (ULONG) varnt.VarDataSize();
}
else
{
// PERFFIX - CopyOrCorece should supply the output length!
*pulLength = GetValueSize();
}
}
}
}
Win4Assert( rDstColumn.IsStatusStored() );
Win4Assert( IsStatusStored() );
rDstColumn.SetStatus( pbDstRow, GetStatus( pbSrcRow ) );
}
tbDebugOut(( DEB_ITRACE, "ColumnStatus: 0x%x\n",
rDstColumn.GetStatus( pbDstRow ) ));
}
#if 0 // we never bind to size/length and NOT bind to value
else
{
//
// Destination doesn't need value, check to see if it needs the
// length or status. Get it from the input if it's there.
// Assert if the required value is not there.
//
// NOTE: These values will be valid only for a DBTYPE_VARIANT
// result.
//
if (rDstColumn.IsLengthStored())
{
Win4Assert(VT_VARIANT == rDstColumn.GetStoredType());
ULONG *pulLength = (ULONG *)
(pbDstRow + rDstColumn.GetLengthOffset());
if (IsLengthStored())
*pulLength = *(ULONG *) (pbSrcRow + GetLengthOffset());
else
{
Win4Assert(IsValueStored());
CTableVariant varnt;
XCompressFreeVariant xpVarnt;
if ( CreateVariant(varnt, pbSrcRow, rSrcPool) )
xpVarnt.Set(GetCompressor(), &varnt);
USHORT flags,cbWidth,cbAlign;
CTableVariant::VartypeInfo( varnt.vt,
cbWidth,
cbAlign,
flags );
if ( CTableVariant::TableIsStoredInline( flags, varnt.vt ) )
*pulLength = (ULONG) cbWidth;
else
*pulLength = (ULONG) varnt.VarDataSize();
}
}
// fill-in the column status
Win4Assert( rDstColumn.IsStatusStored() );
Win4Assert( IsStatusStored() );
rDstColumn.SetStatus( pbDstRow, GetStatus( pbSrcRow ) );
}
#endif // 0: we never bind to size/length and NOT bind to value
return DstStatus;
} //CopyColumn
//+-------------------------------------------------------------------------
//
// Member: CTableColumn::CreateVariant, public
//
// Synopsis: Create a table variant from a source column.
//
// Arguments: [rVarnt] -- reference to variant structure to be filled
// [pbSrc] -- source row buffer
// [rSrcPool] -- source variable data allocator
//
// Returns: BOOL -- TRUE if variant needs to be freed by
// column compressor.
//
// Notes: CLEANCODE - Should this routine take an XCompressFreeVariant as an
// input parameter to guard against memory leaks? An
// argument against is that HROW buffers would never
// be compressed, and don't need to know about column
// compressors.
//
// History: 22 Feb 1995 AlanW Created
//
//--------------------------------------------------------------------------
BOOL CTableColumn::CreateVariant(
CTableVariant & rVarnt,
BYTE * pbSrc,
PVarAllocator & rSrcPool
) const {
//
// Advance the row buffer pointer to the stored value data.
//
Win4Assert(IsValueStored());
BYTE * pbSrcRow = pbSrc;
pbSrc += GetValueOffset();
if ( IsCompressedCol() )
{
GetValueResult eGvr = GetCompressor()->GetData( &rVarnt,
GetStoredType(),
GetValueSize()?
*((ULONG *) (pbSrc)):
0,
PropId);
if ( GVRSuccess != eGvr )
{
rVarnt.vt = VT_EMPTY;
}
//
// Set up to free the variant when we're done with it.
//
return TRUE;
}
else
{
//
// There is no compression.
//
if (VT_VARIANT == GetStoredType())
{
Win4Assert(GetValueSize() == sizeof PROPVARIANT);
rVarnt = *((CTableVariant *) (pbSrc));
}
else
{
if ( IsNull( pbSrcRow ) )
rVarnt.vt = VT_EMPTY;
else
rVarnt.Init( GetStoredType(), pbSrc, GetValueSize() );
}
if ( rVarnt.VariantPointerInFirstWord() &&
GetStoredType() != VT_CLSID) // already stored as pointer
{
if (0 == rVarnt.pszVal)
{
rVarnt.vt = VT_EMPTY;
tbDebugOut(( DEB_WARN,
"null indirect value for propid %d\n",
PropId ));
}
else
{
rVarnt.pszVal = (LPSTR)
rSrcPool.OffsetToPointer((ULONG_PTR)rVarnt.pszVal);
}
}
else if (rVarnt.VariantPointerInSecondWord())
{
if (0 == rVarnt.blob.pBlobData)
{
rVarnt.vt = VT_EMPTY;
tbDebugOut(( DEB_WARN,
"null indirect value for propid %d\n",
PropId ));
}
else
{
rVarnt.blob.pBlobData = (BYTE *)
rSrcPool.OffsetToPointer((ULONG_PTR)rVarnt.blob.pBlobData);
}
}
} // no compression
return FALSE;
} // CreateVariant