//+--------------------------------------------------------------------------- // // 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 #pragma hdrstop #include #include #include #include #include #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 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