windows-nt/Source/XPSP1/NT/inetsrv/query/cursor/convert.cxx

555 lines
15 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1991 - 2000.
//
// File: CONVERT.CXX
//
// Contents: Restriction to cursor converter
//
// Classes: CConverter
//
// History: 16-Jul-92 MikeHew Created
// 01-Feb-93 KyleP Convert restrictions
//
//----------------------------------------------------------------------------
#include <pch.cxx>
#pragma hdrstop
#include <curstk.hxx>
#include <convert.hxx>
#include <ocursor.hxx>
#include <querble.hxx>
#include <cudebug.hxx>
#include "phrcur.hxx"
#include "andcur.hxx"
#include "orcursor.hxx"
#include "veccurs.hxx"
#include "proxcur.hxx"
#include "andncur.hxx"
//+---------------------------------------------------------------------------
//
// Member: CConverter::CConverter, public
//
// Synopsis:
//
// Arguments: [pQuerble] -- Index
// [cMaxNodes] -- Maximum number of nodes to build
//
// History: 15-Jul-92 MikeHew Created
//
//----------------------------------------------------------------------------
CConverter::CConverter( CQueriable * pQuerble, ULONG cMaxNodes )
: _pQuerble( pQuerble ),
_cNodesRemaining( cMaxNodes )
{
} //CConverter
//+---------------------------------------------------------------------------
//
// Member: CConverter::QueryCursor, public
//
// Synopsis: Walk the query tree, create cursor tree
//
// Arguments: [pRst] -- Tree of query restrictions
//
// History: 15-Jul-92 MikeHew Created
//
//----------------------------------------------------------------------------
CCursor* CConverter::QueryCursor( CRestriction const * pRst )
{
//
// go through leaves, get cursors from index
// combine them into a cursor tree
//
if ( 0 != pRst )
return ConvertRst( pRst );
return 0;
} //QueryCursor
//+---------------------------------------------------------------------------
//
// Member: CConverter::ConvertRst, private
//
// Synopsis: Walk the query tree, create cursor tree
//
// Arguments: [pRst] -- Tree of query restrictions
//
// History: 15-Jul-92 MikeHew Created
//
//----------------------------------------------------------------------------
CCursor* CConverter::ConvertRst( CRestriction const * pRst )
{
TRY
{
if ( pRst->IsLeaf() )
return ConvertLeaf ( pRst );
switch ( pRst->Type() )
{
case RTPhrase:
return ConvertPhraseNode ( pRst->CastToNode() );
case RTProximity:
return ConvertProxNode ( pRst->CastToNode() );
case RTVector:
return ConvertVectorNode ( pRst->CastToNode() );
case RTAnd:
case RTOr:
return ConvertNode ( pRst->CastToNode() );
default:
cuDebugOut(( DEB_ERROR,
"Restriction type %d cannot be converted to cursor\n", pRst->Type() ));
THROW( CException( QUERY_E_INVALIDRESTRICTION ) );
}
}
CATCH( CException, e )
{
if ( !TooManyNodes() )
RETHROW();
}
END_CATCH
return 0;
} //ConvertRst
//+---------------------------------------------------------------------------
//
// Member: CConverter::ConvertPhraseNode, private
//
// Synopsis: Convert a phrase node to a COccCursor.
//
// Arguments: [pNodeRst] -- Restriction
//
// Returns: COccCursor
//
// History: 19-Sep-91 BartoszM Created.
// 15-Apr-92 AmyA Changed from ConvertOccNode and return
// value from CCursor. Moved code for
// proximity node to ConvertProxNode.
// 16-Jul-92 MikeHew Yanked out of CQParse and put into
// CConverter
//
//----------------------------------------------------------------------------
COccCursor* CConverter::ConvertPhraseNode( CNodeRestriction const * pNodeRst )
{
Win4Assert( RTPhrase == pNodeRst->Type() );
unsigned cChild = pNodeRst->Count();
if ( cChild == 0 )
return 0;
if ( cChild == 1 )
return ConvertLeaf ( pNodeRst->GetChild(0) );
COccCurStack curStack ( cChild );
// Get all the cursors
for ( unsigned i = 0; i < cChild; i++ )
{
CRestriction* pChild = pNodeRst->GetChild(i);
COccCursor* pCur = ConvertLeaf ( pChild );
if ( pCur == 0 )
break;
curStack.Push( pCur );
}
// Combine all the cursors
unsigned cCur = curStack.Count();
if ( cCur < cChild )
return 0;
XArray<OCCURRENCE> aOcc (cCur);
CWordRestriction* wordRst = (CWordRestriction*) pNodeRst->GetChild(0);
OCCURRENCE occStart = wordRst->Occurrence();
for ( unsigned k = 0; k < cCur; k++ )
{
wordRst = (CWordRestriction*) pNodeRst->GetChild(k);
aOcc[k] = wordRst->Occurrence() - occStart;
}
return new CPhraseCursor( curStack, aOcc );
} //ConvertPhraseNode
//+---------------------------------------------------------------------------
//
// Member: CConverter::ConvertProxNode, private
//
// Synopsis: Convert a Proximity node into a CCursor.
//
// Arguments: [pNodeRst] -- Restriction
//
// Returns: CCursor
//
// History: 15-Apr-92 AmyA Created.
// 16-Jul-92 MikeHew Yanked out of CQParse and put into
// CConverter
//
//----------------------------------------------------------------------------
CCursor* CConverter::ConvertProxNode( CNodeRestriction const * pNodeRst )
{
Win4Assert ( pNodeRst->Type() == RTProximity );
unsigned cChild = pNodeRst->Count();
if ( cChild == 0 )
return 0;
// We don't support queries like: foo ~ !bar
if ( cChild == 1 )
{
CRestriction * pRst = pNodeRst->GetChild(0);
if ( pRst->IsLeaf() )
return ConvertLeaf( pRst );
else if ( RTPhrase == pRst->Type() )
return ConvertPhraseNode ( pRst->CastToNode() );
else
THROW( CException( QUERY_E_INVALIDRESTRICTION ) );
}
COccCurStack curStack ( cChild );
// Get all the cursors
for ( unsigned i = 0; i < cChild; i++ )
{
CRestriction * pChild = pNodeRst->GetChild(i);
COccCursor * pCur;
if ( pChild->IsLeaf() )
pCur = ConvertLeaf( pChild );
else if ( RTPhrase == pChild->Type() )
pCur = ConvertPhraseNode ( pChild->CastToNode() );
else
THROW( CException( QUERY_E_INVALIDRESTRICTION ) );
if ( pCur != 0 )
curStack.Push(pCur);
}
// Combine all the cursors
unsigned cCur = curStack.Count();
if ( cCur < cChild )
{
cuDebugOut (( DEB_ITRACE, "prox:Fewer cursors than expected\n" ));
return 0;
}
return new CProxCursor ( cCur, curStack );
} //ConvertProxNode
//+---------------------------------------------------------------------------
//
// Member: CConverter::ConvertAndNotNode, private
//
// Synopsis: Convert an And Not node into a CCursor.
//
// Arguments: [pNodeRst] -- Restriction
//
// Returns: CCursor
//
// Notes: Will return 0 if there is not exactly two children nodes.
//
// History: 22-Apr-92 AmyA Created.
// 16-Jul-92 MikeHew Yanked out of CQParse and put into
// CConverter
//
//----------------------------------------------------------------------------
CCursor* CConverter::ConvertAndNotNode( XCursor & curSrc, CCurStack & curNot )
{
Win4Assert( curNot.Count() > 0 );
XCursor Cur;
//
// Note we should convert (a & b & !c) to (a & ( b & !c ) ),
// Also, convert !a & b to b & !a
// This code has been substantially rewritten in Babylon.
//
while ( curNot.Count() > 0 )
{
XCursor curFilter( curNot.Pop() );
//
// WARNING: Don't put any code between the next two lines if that
// code can THROW.
//
CCursor * pTemp = new CAndNotCursor( curSrc, curFilter );
curSrc.Set( pTemp );
}
return curSrc.Acquire();
} //ConvertAndNotNode
//+---------------------------------------------------------------------------
//
// Member: CConverter::ConvertVectorNode, private
//
// Synopsis: Convert an Or, And, or AndNot node into a CCursor.
//
// Arguments: [pNodeRst] -- Restriction
//
// Returns: CCursor
//
// History: 21-Oct-92 KyleP Created.
//
// Notes: This function is very similar to ConvertNode. The main
// difference is that noise Restrictions have been preserved
// in the vector input and maintain their position as place
// holders in the vector.
//
//----------------------------------------------------------------------------
CCursor* CConverter::ConvertVectorNode( CNodeRestriction const * pNodeRst )
{
unsigned cChild = pNodeRst->Count();
if ( cChild == 0 )
return 0;
CCurStack curStack ( cChild );
// Get all the cursors
for ( unsigned i = 0; i < cChild; i++ )
{
CRestriction* pChild = pNodeRst->GetChild(i);
CCursor* pCur = pChild ? ConvertRst ( pChild ) : 0;
if ( pCur != 0 )
{
ULONG wt = pChild->Weight();
pCur->SetWeight( min( wt, MAX_QUERY_RANK ) );
}
curStack.Push(pCur);
}
// Combine all the cursors
Win4Assert( curStack.Count() == cChild );
return new CVectorCursor( cChild,
curStack,
((CVectorRestriction *)
pNodeRst)->RankMethod() );
} //ConvertVectorNode
//+---------------------------------------------------------------------------
//
// Member: CConverter::ConvertNode, private
//
// Synopsis: Convert an Or, And, or AndNot node into a CCursor.
//
// Arguments: [pNodeRst] -- Restriction
//
// Returns: CCursor
//
// History: 19-Sep-91 BartoszM Created.
// 23-Jun-92 MikeHew Added weight transfering.
// 16-Jul-92 MikeHew Yanked out of CQParse and put into
// CConverter
//
//----------------------------------------------------------------------------
CCursor* CConverter::ConvertNode( CNodeRestriction const * pNodeRst )
{
unsigned cChild = pNodeRst->Count();
if ( cChild == 0 )
return 0;
if ( cChild == 1 )
return ConvertRst ( pNodeRst->GetChild(0) );
BOOL fNullCursor = FALSE;
BOOL fNullNotCursor = FALSE;
CCurStack curStack ( cChild );
CCurStack curNot( 1 );
// Get all the cursors
for ( unsigned i = 0; i < cChild; i++ )
{
CRestriction* pChild = pNodeRst->GetChild(i);
CCursor * pCur = 0;
if ( pChild->Type() == RTNot )
{
pChild = ((CNotRestriction *)pChild)->GetChild();
pCur = ConvertRst( pChild );
if ( 0 == pCur )
fNullNotCursor = TRUE;
else
curNot.Push(pCur);
}
else
{
pCur = ConvertRst( pChild );
if ( 0 == pCur )
fNullCursor = TRUE;
else
curStack.Push(pCur);
}
if ( 0 != pCur )
{
ULONG wt = pChild->Weight();
pCur->SetWeight( min( wt, MAX_QUERY_RANK ) );
}
}
//
// Combine all the cursors
//
unsigned cCur = curStack.Count();
switch ( pNodeRst->Type() )
{
case RTAnd:
{
if ( curStack.Count() == 0
&& !fNullCursor
&& (curNot.Count() > 0 || fNullNotCursor) )
{
//
// !cat & !dog is an invalid content restriction
//
cuDebugOut(( DEB_ERROR,
"Content AND combined with only NOT nodes\n" ));
THROW( CException( QUERY_E_INVALIDRESTRICTION ) );
}
if ( fNullCursor || curStack.Count() == 0 )
return 0;
XCursor cur;
if ( curStack.Count() == 1 )
cur.Set( curStack.Pop() );
else
cur.Set( new CAndCursor ( cCur, curStack ) );
if ( curNot.Count() > 0 )
return( ConvertAndNotNode( cur, curNot ) );
else
return( cur.Acquire() );
}
case RTOr:
{
if ( curNot.Count() > 0 )
{
cuDebugOut(( DEB_ERROR,
"Content NOT combined with OR node. Must be AND.\n",
pNodeRst->Type() ));
THROW( CException( QUERY_E_INVALIDRESTRICTION ) );
}
if ( 0 == cCur )
return 0;
if ( cCur == 1 )
return curStack.Pop();
return new COrCursor ( cCur, curStack );
}
default:
cuDebugOut(( DEB_ERROR,
"Restriction type %d not implemented\n",
pNodeRst->Type() ));
THROW( CException( QUERY_E_INVALIDRESTRICTION ) );
}
return 0;
} //ConvertNode
//+---------------------------------------------------------------------------
//
// Member: CConverter::ConvertLeaf, private
//
// Synopsis: Convert a leaf node to a cursor
//
// Arguments: [pNodeRst] -- Restriction
//
// Returns: cursor
//
// History: 19-Sep-91 BartoszM Created.
// 16-Jul-92 MikeHew Yanked out of CQParse and put into
// CConverter
//
//----------------------------------------------------------------------------
COccCursor* CConverter::ConvertLeaf( CRestriction const * pRst )
{
if ( TooManyNodes() )
{
ciDebugOut(( DEB_WARN, "Node limit reached (detected) in CConverter::ConverLeaf.\n" ));
THROW( CException( STATUS_TOO_MANY_NODES ) );
}
switch ( pRst->Type() )
{
case RTWord:
{
CWordRestriction* wordRst = (CWordRestriction*) pRst;
const CKey* pKey = wordRst->GetKey();
return _pQuerble->QueryCursor( pKey, wordRst->IsRange(), _cNodesRemaining );
}
case RTSynonym:
{
CSynRestriction* pSynRst = (CSynRestriction*) pRst;
CKeyArray& keyArray = pSynRst->GetKeys();
return _pQuerble->QuerySynCursor ( keyArray, pSynRst->IsRange(), _cNodesRemaining );
}
case RTRange:
{
CRangeRestriction* pRangRst = (CRangeRestriction*) pRst;
COccCursor * pCursor = _pQuerble->QueryRangeCursor ( pRangRst->GetStartKey(),
pRangRst->GetEndKey(),
_cNodesRemaining );
if( 0 != pCursor && pidUnfiltered == pRangRst->Pid() )
pCursor->SetUnfilteredOnly( TRUE );
return pCursor;
}
default:
cuDebugOut(( DEB_ITRACE, "Wrong type for occurrence leaf\n" ));
return 0;
}
} //ConvertLeaf