1081 lines
32 KiB
C++
1081 lines
32 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1995 - 2000.
|
|
//
|
|
// File: Parstree.cxx
|
|
//
|
|
// Contents: Converts OLE-DB command tree into CColumns, CSort, CRestriction
|
|
// and CCategorization.
|
|
//
|
|
// Classes: CParseCommandTree
|
|
//
|
|
// History: 31 May 95 AlanW Created
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
#include <pch.cxx>
|
|
#pragma hdrstop
|
|
|
|
#include <coldesc.hxx>
|
|
#include <pidmap.hxx>
|
|
#include <parstree.hxx>
|
|
|
|
static GUID guidBmk = DBBMKGUID;
|
|
const CFullPropSpec colChapter( guidBmk, PROPID_DBBMK_CHAPTER );
|
|
const CFullPropSpec colBookmark( guidBmk, PROPID_DBBMK_BOOKMARK );
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::~CParseCommandTree, public
|
|
//
|
|
// Synopsis: Destructor of CParseCommandTree
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CParseCommandTree::~CParseCommandTree( void )
|
|
{
|
|
if (_prst) delete _prst;
|
|
}
|
|
|
|
|
|
//--------------------------------------------------------------------------
|
|
//
|
|
// Command tree syntax accepted:
|
|
//
|
|
// QueryTree :
|
|
// Table |
|
|
// ContentTable |
|
|
// SetUnion |
|
|
// Categorization |
|
|
// Projection |
|
|
// OrderedQueryTree |
|
|
// Restriction |
|
|
// TopNode QueryTree
|
|
// Categorization :
|
|
// nesting OrderedQueryTree GroupingList ParentList ChildList coldef
|
|
// ContentTable :
|
|
// <empty> |
|
|
// scope_list_anchor scope_list_element |
|
|
// ScopeList scope_list_element
|
|
// GroupingList : ProjectList
|
|
// ParentList : ProjectList
|
|
// ChildList : ProjectList
|
|
// Projection :
|
|
// ProjectOperator QueryTree ProjectList
|
|
// ProjectOperator :
|
|
// project | project_order_preserving
|
|
// ProjectList :
|
|
// project_list_anchor project_list_element |
|
|
// ProjectList project_list_element
|
|
// OrderedQueryTree :
|
|
// sort QueryTree SortList
|
|
// ScopeList :
|
|
// scope_list_anchor scope_list_element |
|
|
// ScopeList scope_list_element
|
|
// SetUnion:
|
|
// ContentTable ContentTable |
|
|
// SetUnion ContentTable
|
|
// SortList :
|
|
// sort_list_anchor sort_list_element |
|
|
// SortList sort_list_element
|
|
// Restriction :
|
|
// select QueryTree ExprTree |
|
|
// select_order_preserving QueryTree ExprTree
|
|
// Table :
|
|
// scalar_identifier( Table )
|
|
// ExprTree :
|
|
// not ExprTree |
|
|
// and ExprList |
|
|
// or ExprList |
|
|
// content_proximity ContentExprList
|
|
// vector_or ContentExprList
|
|
// ExprTerm
|
|
// ExprList :
|
|
// ExprTree ExprTree |
|
|
// ExprList ExprTree
|
|
// ExprTerm :
|
|
// ContentExpr |
|
|
// ValueExpr |
|
|
// LikeExpr
|
|
// ValueExpr :
|
|
// ValueOperator scalar_identifier(ColId) scalar_constant
|
|
// ValueOperator :
|
|
// equal | not_equal | less | less_equal | greater | greater_equal |
|
|
// equal_any | not_equal_any | less_any | less_equal_any |
|
|
// greater_any | greater_equal_any |
|
|
// equal_all | not_equal_all | less_all | less_equal_all |
|
|
// greater_all | greater_equal_all | any_bits | all_bits
|
|
// LikeExpr :
|
|
// like(OfsRegexp) scalar_identifier(ColId) scalar_constant
|
|
// ContentExprTree :
|
|
// not ContentExprTree |
|
|
// and ContentExprList |
|
|
// or ContentExprList |
|
|
// content_proximity ContentExprList
|
|
// ContentExpr
|
|
// ContentExprList :
|
|
// ContentExprTree ContentExprTree |
|
|
// ContentExprList ContentExprTree
|
|
// ContentExpr :
|
|
// content PhraseList |
|
|
// freetext_content PhraseList
|
|
//
|
|
//+---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::ParseTree, public
|
|
//
|
|
// Synopsis: Parse a CDbCmdTreeNode into projection, sort, restriction
|
|
// and categorizations.
|
|
//
|
|
// Arguments: [pTree] -- CDbCmdTreeNode node at root of tree to be parsed
|
|
//
|
|
// Returns: nothing. Throws if error in tree.
|
|
//
|
|
// History: 31 May 95 AlanW Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CParseCommandTree::ParseTree( CDbCmdTreeNode * pTree )
|
|
{
|
|
CheckRecursionLimit();
|
|
|
|
if (pTree == 0)
|
|
{
|
|
vqDebugOut(( DEB_WARN, "ParseTree - null tree\n"));
|
|
THROW( CException( E_INVALIDARG ));
|
|
}
|
|
|
|
switch (pTree->GetCommandType())
|
|
{
|
|
case DBOP_project:
|
|
case DBOP_project_order_preserving:
|
|
ParseProjection( pTree );
|
|
break;
|
|
|
|
case DBOP_sort:
|
|
ParseSort( pTree );
|
|
break;
|
|
|
|
case DBOP_select:
|
|
// case DBOP_select_order_preserving:
|
|
ParseRestriction( pTree );
|
|
break;
|
|
|
|
case DBOP_nesting:
|
|
ParseCategorization( pTree );
|
|
break;
|
|
|
|
case DBOP_table_name:
|
|
if ( _wcsicmp( ((CDbTableId *)pTree)->GetTableName(), DBTABLEID_NAME ))
|
|
SetError( pTree );
|
|
break;
|
|
|
|
case DBOP_top:
|
|
ParseTopNode( pTree );
|
|
break;
|
|
|
|
case DBOP_firstrows:
|
|
ParseFirstRowsNode( pTree );
|
|
break;
|
|
|
|
case DBOP_content_table:
|
|
ParseScope( pTree );
|
|
break;
|
|
|
|
case DBOP_set_union:
|
|
ParseMultiScopes( pTree );
|
|
break;
|
|
|
|
default:
|
|
vqDebugOut(( DEB_WARN, "ParseTree - unexpected operator %d\n", pTree->GetCommandType() ));
|
|
SetError( pTree );
|
|
break;
|
|
}
|
|
return;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::ParseScope, public
|
|
//
|
|
// Synopsis: Parse a CDbCmdTreeNode scope operator
|
|
//
|
|
// Arguments: [pTree] -- CDbCmdTreeNode scope specification node
|
|
//
|
|
// History: 09-15-98 danleg Created
|
|
//
|
|
// Notes: Here is a variation of a tree with scope nodes. This routine
|
|
// gets a tree rooted either at a content_table node if a single
|
|
// scope is specified, or a set_union node if there are multiple
|
|
// scopes.
|
|
//
|
|
// proj
|
|
// _________________/
|
|
// /
|
|
// select ___________________ LA-proj
|
|
// _____________/ ____/
|
|
// / /
|
|
// set_union ________ content LE_proj ____ LE_proj
|
|
// ____/ ____/ ___/ __/
|
|
// / / / /
|
|
// / column_name column_name column_name
|
|
// /
|
|
// set_union ________________ content_table
|
|
// __/ __/
|
|
// / /
|
|
// / ...
|
|
// content_table ____ content_table
|
|
// ___/ ___/
|
|
// / /
|
|
// LA_scp LA_scp
|
|
// __/ __/
|
|
// / /
|
|
// LE_scp ... LE_scp ___ LE_scp ___ ...
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CParseCommandTree::ParseScope( CDbCmdTreeNode * pTree )
|
|
{
|
|
CheckRecursionLimit();
|
|
|
|
Win4Assert( DBOP_content_table == pTree->GetCommandType() );
|
|
|
|
if ( 0 != pTree->GetFirstChild() )
|
|
{
|
|
//
|
|
// qualified scope
|
|
//
|
|
|
|
ParseScopeListElements( pTree );
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// unqualified scope
|
|
//
|
|
|
|
_cScopes++;
|
|
|
|
_xaMachines.SetSize( _cScopes );
|
|
_xaScopes.SetSize( _cScopes );
|
|
_xaFlags.SetSize( _cScopes );
|
|
_xaCatalogs.SetSize( _cScopes );
|
|
|
|
CDbContentTable * pCntntTbl =
|
|
(CDbContentTable *) (pTree->CastToStruct())->value.pdbcntnttblValue;
|
|
|
|
_xaMachines[_cScopes-1] = pCntntTbl->GetMachine();
|
|
_xaCatalogs[_cScopes-1] = pCntntTbl->GetCatalog();
|
|
_xaFlags[_cScopes-1] = QUERY_DEEP | QUERY_PHYSICAL_PATH;
|
|
_xaScopes[_cScopes-1] = L"\\";
|
|
}
|
|
|
|
if ( 0 != pTree->GetNextSibling() &&
|
|
DBOP_content_table == pTree->GetNextSibling()->GetCommandType() )
|
|
ParseTree( pTree->GetNextSibling() );
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::ParseScopeListElements, public
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [pcntntTbl] -- node consisting of catalog/machine name info
|
|
// [pTree] -- CDbCmdTreeNode scope specification node
|
|
//
|
|
// History: 01-24-99 danleg Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CParseCommandTree::ParseScopeListElements( CDbCmdTreeNode * pTree )
|
|
{
|
|
CheckRecursionLimit();
|
|
|
|
if ( 0 == pTree->GetFirstChild() )
|
|
{
|
|
SetError( pTree );
|
|
}
|
|
if ( DBOP_scope_list_anchor != pTree->GetFirstChild()->GetCommandType() )
|
|
{
|
|
SetError( pTree->GetFirstChild() );
|
|
}
|
|
CheckOperatorArity( pTree->GetFirstChild(), -1 );
|
|
|
|
CDbContentTable * pCntntTbl =
|
|
(CDbContentTable *) (pTree->CastToStruct())->value.pdbcntnttblValue;
|
|
CDbCmdTreeNode * pLE_SCP = pTree->GetFirstChild()->GetFirstChild();
|
|
|
|
for ( ;
|
|
pLE_SCP;
|
|
pLE_SCP = pLE_SCP->GetNextSibling() )
|
|
{
|
|
if ( DBOP_scope_list_element != pLE_SCP->GetCommandType() ||
|
|
0 != pLE_SCP->GetFirstChild() )
|
|
{
|
|
SetError( pLE_SCP );
|
|
}
|
|
VerifyValueType( pLE_SCP, DBVALUEKIND_CONTENTSCOPE );
|
|
|
|
CDbContentScope * pCntntScp =
|
|
(CDbContentScope *) (pLE_SCP->CastToStruct())->value.pdbcntntscpValue;
|
|
|
|
_cScopes++;
|
|
|
|
_xaMachines.SetSize( _cScopes );
|
|
_xaScopes.SetSize( _cScopes );
|
|
_xaFlags.SetSize( _cScopes );
|
|
_xaCatalogs.SetSize( _cScopes );
|
|
|
|
_xaMachines[_cScopes-1] = pCntntTbl->GetMachine();
|
|
_xaCatalogs[_cScopes-1] = pCntntTbl->GetCatalog();
|
|
|
|
//
|
|
// DBPROP_CI_SCOPE_FLAGS
|
|
//
|
|
if ( pCntntScp->GetType() & SCOPE_TYPE_WINPATH )
|
|
_xaFlags[_cScopes-1] = QUERY_PHYSICAL_PATH;
|
|
else if ( pCntntScp->GetType() & SCOPE_TYPE_VPATH )
|
|
_xaFlags[_cScopes-1] = QUERY_VIRTUAL_PATH;
|
|
else
|
|
{
|
|
// unknown flag
|
|
SetError( pLE_SCP );
|
|
}
|
|
|
|
if ( pCntntScp->GetFlags() & SCOPE_FLAG_DEEP )
|
|
_xaFlags[_cScopes-1] |= QUERY_DEEP;
|
|
else
|
|
_xaFlags[_cScopes-1] |= QUERY_SHALLOW;
|
|
|
|
//
|
|
// DBPROP_CI_INCLUDE_SCOPES
|
|
//
|
|
|
|
_xaScopes[_cScopes-1] = pCntntScp->GetValue();
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::ParseMultiScopes, public
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [pcntntTbl] -- node consisting of catalog/machine name info
|
|
// [pTree] -- CDbCmdTreeNode scope specification node
|
|
//
|
|
// History: 01-24-99 danleg Created
|
|
//
|
|
// Notes: Two possibilities:
|
|
//
|
|
// set_union ___ content
|
|
// ______________/
|
|
// /
|
|
// content_table ___ content_table ___ ... ___ content_table
|
|
//
|
|
//
|
|
// -- or --
|
|
//
|
|
// set_union ___ content
|
|
// ___________/
|
|
// /
|
|
// set_union _____________________________ set_union
|
|
// _____/ ______/
|
|
// / /
|
|
// content_table ___ content_table content_table ___ content_table
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CParseCommandTree::ParseMultiScopes( CDbCmdTreeNode * pTree )
|
|
{
|
|
CheckRecursionLimit();
|
|
|
|
Win4Assert( DBOP_set_union == pTree->GetCommandType() );
|
|
|
|
Win4Assert( 0 != pTree->GetFirstChild() &&
|
|
0 != pTree->GetNextSibling() );
|
|
|
|
if ( DBOP_content_table == pTree->GetCommandType() )
|
|
{
|
|
CDbCmdTreeNode * pCntntTbl = pTree->GetFirstChild();
|
|
|
|
for ( ;
|
|
pCntntTbl;
|
|
pCntntTbl = pCntntTbl->GetNextSibling() )
|
|
{
|
|
if ( DBOP_content_table != pCntntTbl->GetCommandType() )
|
|
SetError( pCntntTbl );
|
|
|
|
ParseScope( pCntntTbl );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Win4Assert( DBOP_set_union == pTree->GetCommandType() );
|
|
|
|
if ( DBOP_content_table != pTree->GetFirstChild()->GetCommandType() &&
|
|
DBOP_set_union != pTree->GetFirstChild()->GetCommandType() )
|
|
SetError( pTree );
|
|
|
|
ParseTree( pTree->GetFirstChild() );
|
|
|
|
//
|
|
// Note that the DBOP_content branch gets parsed by ParseRestriction
|
|
//
|
|
if ( DBOP_content_table == pTree->GetNextSibling()->GetCommandType() ||
|
|
DBOP_set_union == pTree->GetNextSibling()->GetCommandType() )
|
|
ParseTree( pTree->GetNextSibling() );
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::ParseProjection, public
|
|
//
|
|
// Synopsis: Parse a CDbCmdTreeNode project operator
|
|
//
|
|
// Arguments: [pTree] -- CDbCmdTreeNode node at root of tree to be parsed
|
|
//
|
|
// History: 31 May 95 AlanW Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CParseCommandTree::ParseProjection( CDbCmdTreeNode * pTree )
|
|
{
|
|
CheckRecursionLimit();
|
|
|
|
CheckOperatorArity( pTree, 2 );
|
|
|
|
CDbCmdTreeNode * pChild = pTree->GetFirstChild();
|
|
|
|
//
|
|
// Parse the projection list first, so a projection higher in the
|
|
// tree takes precedence. A projection is not permitted in a tree
|
|
// that also contains a nesting node.
|
|
//
|
|
// Should a higher level projection take precedence?
|
|
// We currently return an error.
|
|
//
|
|
if (_categ.Count() == 0 && _cols.Count() == 0)
|
|
ParseProjectList ( pChild->GetNextSibling(), _cols );
|
|
else
|
|
SetError( pTree );
|
|
|
|
ParseTree( pChild );
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::ParseProjectList, public
|
|
//
|
|
// Synopsis: Parse a CDbCmdTreeNode project list
|
|
//
|
|
// Arguments: [pTree] -- CDbCmdTreeNode node for project list head
|
|
// [Cols] -- CColumnSet in which columns are collected
|
|
//
|
|
// History: 31 May 95 AlanW Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CParseCommandTree::ParseProjectList( CDbCmdTreeNode *pTree, CColumnSet& Cols )
|
|
{
|
|
CheckRecursionLimit();
|
|
|
|
if (pTree->GetCommandType() != DBOP_project_list_anchor ||
|
|
pTree->GetFirstChild() == 0)
|
|
{
|
|
SetError( pTree );
|
|
}
|
|
|
|
CFullPropSpec Col;
|
|
|
|
CDbProjectListElement* pList = (CDbProjectListElement *) pTree->GetFirstChild();
|
|
for ( ;
|
|
pList;
|
|
pList = (CDbProjectListElement *)pList->GetNextSibling())
|
|
{
|
|
if ( pList->GetCommandType() != DBOP_project_list_element ||
|
|
pList->GetFirstChild() == 0 )
|
|
{
|
|
SetError( pList );
|
|
}
|
|
|
|
CDbCmdTreeNode * pColumn = pList->GetFirstChild();
|
|
if ( !pColumn->IsColumnName() )
|
|
{
|
|
SetError( pColumn );
|
|
}
|
|
|
|
// Add element to projection list
|
|
PROPID pid = GetColumnPropSpec(pColumn, Col);
|
|
Cols.Add( pid, Cols.Count() );
|
|
|
|
if ( 0 != pList->GetName() )
|
|
_pidmap.SetFriendlyName( pid, pList->GetName() );
|
|
}
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::ParseSort, public
|
|
//
|
|
// Synopsis: Parse a CDbCmdTreeNode sort node
|
|
//
|
|
// Arguments: [pTree] -- CDbCmdTreeNode node for sort list head
|
|
//
|
|
// Notes: Sort nodes are added to the CSortSet in private data
|
|
//
|
|
// History: 31 May 95 AlanW Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CParseCommandTree::ParseSort( CDbCmdTreeNode * pTree )
|
|
{
|
|
CheckRecursionLimit();
|
|
|
|
CheckOperatorArity( pTree, 2 );
|
|
CDbCmdTreeNode * pChild = pTree->GetFirstChild();
|
|
|
|
//
|
|
// Parse the sort list first, so a sort higher in the
|
|
// tree is primary.
|
|
//
|
|
ParseSortList ( pChild->GetNextSibling() );
|
|
ParseTree( pChild );
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::ParseSortList, public
|
|
//
|
|
// Synopsis: Parse a CDbCmdTreeNode sort list
|
|
//
|
|
// Arguments: [pTree] -- CDbCmdTreeNode node for sort list head
|
|
//
|
|
// Notes: Sort nodes are added to the CSortSet in private data
|
|
//
|
|
// History: 31 May 95 AlanW Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CParseCommandTree::ParseSortList( CDbCmdTreeNode * pTree )
|
|
{
|
|
CheckRecursionLimit();
|
|
|
|
if (pTree->GetCommandType() != DBOP_sort_list_anchor ||
|
|
pTree->GetFirstChild() == 0)
|
|
{
|
|
SetError( pTree );
|
|
}
|
|
|
|
CDbSortListAnchor *pSortAnchor = (CDbSortListAnchor *) pTree;
|
|
|
|
unsigned i = 0;
|
|
for (CDbCmdTreeNode * pSortList = pSortAnchor->GetFirstChild();
|
|
pSortList;
|
|
pSortList = pSortList->GetNextSibling(), i++)
|
|
{
|
|
if (pSortList->GetCommandType() != DBOP_sort_list_element)
|
|
{
|
|
SetError( pSortList );
|
|
}
|
|
VerifyValueType( pSortList, DBVALUEKIND_SORTINFO );
|
|
CheckOperatorArity( pSortList, 1 );
|
|
|
|
CDbSortListElement* pSLE = (CDbSortListElement *) pSortList;
|
|
CFullPropSpec Col;
|
|
PROPID pid = GetColumnPropSpec(pSLE->GetFirstChild(), Col);
|
|
|
|
DWORD dwOrder = pSLE->GetDirection() ? QUERY_SORTDESCEND :
|
|
QUERY_SORTASCEND;
|
|
LCID locale = pSLE->GetLocale();
|
|
|
|
if ( _categ.Count() != 0 && i < _sort.Count() )
|
|
{
|
|
//
|
|
// Check that the sort specification matches any set as a result of
|
|
// the categorization (but which may differ in sort direction and
|
|
// locale).
|
|
//
|
|
SSortKey & sortkey = _sort.Get(i);
|
|
if (sortkey.pidColumn != pid)
|
|
{
|
|
SetError(pSLE);
|
|
}
|
|
|
|
if (sortkey.dwOrder != dwOrder)
|
|
sortkey.dwOrder = dwOrder;
|
|
|
|
if (sortkey.locale != locale)
|
|
sortkey.locale = locale;
|
|
}
|
|
else
|
|
{
|
|
// Add element to the sort list
|
|
SSortKey sortkey(pid, dwOrder, locale);
|
|
_sort.Add( sortkey, _sort.Count() );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::ParseRestriction, public
|
|
//
|
|
// Synopsis: Parse a CDbCmdTreeNode select node
|
|
//
|
|
// Arguments: [pTree] -- CDbCmdTreeNode node for select tree
|
|
//
|
|
// History: 31 May 95 AlanW Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CParseCommandTree::ParseRestriction( CDbCmdTreeNode *pTree )
|
|
{
|
|
CheckRecursionLimit();
|
|
|
|
//
|
|
// Only one selection can exist in the tree; there must be exactly
|
|
// two operands: the selection expression and a table identifier.
|
|
// For use with AddPostProcessing, another select should be
|
|
// allowed higher in the tree, with the restrictions anded
|
|
// together.
|
|
//
|
|
if (_prst != 0 )
|
|
SetError( pTree );
|
|
CheckOperatorArity( pTree, 2 );
|
|
VerifyValueType( pTree, DBVALUEKIND_EMPTY );
|
|
|
|
CDbCmdTreeNode * pTable = pTree->GetFirstChild();
|
|
CDbCmdTreeNode * pExpr = pTable->GetNextSibling();
|
|
|
|
_prst = ParseExpression( pExpr );
|
|
|
|
ParseTree( pTable );
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::ParseCategorization, public
|
|
//
|
|
// Synopsis: Parse a CDbCmdTreeNode nesting node
|
|
//
|
|
// Arguments: [pTree] -- CDbCmdTreeNode node for nesting tree
|
|
//
|
|
// History: 31 Jul 1995 AlanW Created
|
|
//
|
|
// Notes: Syntax accepted:
|
|
//
|
|
// Categorization :
|
|
// nesting OrderedQueryTree GroupingList
|
|
// ParentList ChildList coldef
|
|
// GroupingList : ProjectList
|
|
// ParentList : ProjectList
|
|
// ChildList : ProjectList
|
|
//
|
|
// The GroupingList may be a list of columns on which
|
|
// to do a unique value categorization, or a defined-by-guid
|
|
// function that specifies one of the other categorizations.
|
|
// The ParentList may only specify columns in the GroupingList,
|
|
// columns in upper level groupings, or certain aggregations
|
|
// on those columns.
|
|
// The ChildList forms the projectlist for the base table.
|
|
// The coldef node for the nesting column can give only the
|
|
// special column id for the chapter column.
|
|
//
|
|
// Only the unique value categorization is implemented at present.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CParseCommandTree::ParseCategorization( CDbCmdTreeNode *pTree )
|
|
{
|
|
CheckRecursionLimit();
|
|
|
|
CheckOperatorArity( pTree, 5 );
|
|
VerifyValueType( pTree, DBVALUEKIND_EMPTY );
|
|
|
|
//
|
|
// We can't sort or project the output of a categorized table. We can,
|
|
// however permit a sort as a side-effect of an existing
|
|
// nesting node for a unique value categorization.
|
|
//
|
|
if (_categ.Count() == 0 &&
|
|
(_sort.Count() > 0 || _cols.Count() > 0))
|
|
SetError( pTree, E_INVALIDARG );
|
|
|
|
Win4Assert(_sort.Count() == _categ.Count());
|
|
|
|
CDbCmdTreeNode * pTable = pTree->GetFirstChild();
|
|
CDbCmdTreeNode * pGroupingList = pTable->GetNextSibling();
|
|
CDbCmdTreeNode * pParentList = pGroupingList->GetNextSibling();
|
|
CDbCmdTreeNode * pChildList = pParentList->GetNextSibling();
|
|
CDbCmdTreeNode * pNestingColumn = pChildList->GetNextSibling();
|
|
|
|
CheckOperatorArity( pGroupingList, 1 ); // one grouping col. now
|
|
// CheckOperatorArity( pParentList, -1 );
|
|
// CheckOperatorArity( pChildList, -1 );
|
|
CheckOperatorArity( pNestingColumn, 0 );
|
|
|
|
//
|
|
// For now, the only supported categorization is a unique
|
|
// value categorization. For this, the grouping list is
|
|
// just a projection list that gives the unique columns.
|
|
//
|
|
|
|
CColumnSet colGroup;
|
|
ParseProjectList( pGroupingList, colGroup );
|
|
|
|
for (unsigned i = 0; i < colGroup.Count(); i++)
|
|
{
|
|
SSortKey sortkey(colGroup.Get(i), QUERY_SORTASCEND);
|
|
_sort.Add( sortkey, _sort.Count() );
|
|
}
|
|
|
|
//
|
|
// For now, the parent list can only mention columns also
|
|
// in the grouping list (which are now in the sort list).
|
|
// In addition, the bookmark and chapter columns will be
|
|
// available in the parent table.
|
|
// We should also one day allow some aggregations.
|
|
//
|
|
|
|
CColumnSet colParent;
|
|
if (pParentList->GetFirstChild() != 0)
|
|
{
|
|
ParseProjectList( pParentList, colParent );
|
|
if (colParent.Count() > _sort.Count() + 2)
|
|
{
|
|
SetError( pParentList, E_INVALIDARG );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check that the columns are valid
|
|
//
|
|
pParentList = pParentList->GetFirstChild();
|
|
BOOL fChapterFound = FALSE;
|
|
for (i = 0; i < colParent.Count(); i++)
|
|
{
|
|
CFullPropSpec const * pCol = _pidmap.PidToName(colParent.Get(i));
|
|
|
|
if (*pCol == colChapter)
|
|
{
|
|
fChapterFound = TRUE;
|
|
}
|
|
else if (*pCol == colBookmark)
|
|
{
|
|
// Bookmark is permitted in any parent column list
|
|
;
|
|
}
|
|
else
|
|
{
|
|
BOOL fFound = FALSE;
|
|
for (unsigned j = 0; j < _sort.Count(); j++)
|
|
if ( colParent.Get(i) == _sort.Get(j).pidColumn )
|
|
{
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (!fFound)
|
|
SetError(pParentList, E_INVALIDARG);
|
|
}
|
|
|
|
pParentList = pParentList->GetNextSibling();
|
|
}
|
|
if (! fChapterFound)
|
|
colParent.Add( _pidmap.NameToPid(colChapter), colParent.Count());
|
|
|
|
//
|
|
// Columns in the child list replace any existing project list
|
|
// (which can only have been set by a higher-level nesting node).
|
|
//
|
|
|
|
if (_cols.Count())
|
|
{
|
|
vqDebugOut(( DEB_WARN, "CParseCommandTree - "
|
|
"child list in multi-nested command tree ignored\n" ));
|
|
_cols.Clear();
|
|
}
|
|
|
|
if ( 0 != pChildList->GetFirstChild() )
|
|
ParseProjectList( pChildList, _cols );
|
|
|
|
XPtr<CCategSpec> pCatParam = new CUniqueCategSpec( );
|
|
XPtr<CCategorizationSpec> pCat =
|
|
new CCategorizationSpec( pCatParam.GetPointer(),
|
|
colParent.Count() );
|
|
pCatParam.Acquire();
|
|
|
|
for (i = 0; i < colParent.Count(); i++)
|
|
{
|
|
pCat->SetColumn( colParent.Get(i), i );
|
|
}
|
|
|
|
_categ.Add( pCat.GetPointer(), _categ.Count() );
|
|
pCat.Acquire();
|
|
|
|
//
|
|
// Now parse the rest of the tree, and check that _cols was set
|
|
// either here or by a lower level nesting node.
|
|
//
|
|
ParseTree( pTable );
|
|
if ( 0 == _cols.Count() )
|
|
{
|
|
Win4Assert( 0 == pChildList->GetFirstChild() );
|
|
SetError( pChildList );
|
|
}
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::ParseTopNode, public
|
|
//
|
|
// Synopsis: Parse a CDbTopNode operator
|
|
//
|
|
// Arguments: [pTree] -- CDbTopNode node at root of tree to be parsed
|
|
//
|
|
// History: 21 Feb 96 AlanW Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CParseCommandTree::ParseTopNode( CDbCmdTreeNode *pTree )
|
|
{
|
|
CheckRecursionLimit();
|
|
|
|
CheckOperatorArity( pTree, 1 );
|
|
|
|
ULONG cMaxResults = ((CDbTopNode *)pTree)->GetValue();
|
|
if ( cMaxResults == 0 )
|
|
{
|
|
//
|
|
// A query with zero results is uninteresting
|
|
//
|
|
SetError( pTree );
|
|
}
|
|
|
|
//
|
|
// If a top node has already been encountered, then set the
|
|
// limit on results to the minimum of the two Top values
|
|
//
|
|
if ( _cMaxResults == 0 )
|
|
{
|
|
_cMaxResults = cMaxResults;
|
|
}
|
|
else
|
|
{
|
|
if ( cMaxResults < _cMaxResults )
|
|
_cMaxResults = cMaxResults;
|
|
}
|
|
|
|
CDbCmdTreeNode *pChild = pTree->GetFirstChild();
|
|
|
|
ParseTree( pChild );
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::ParseFirstRowsNode, public
|
|
//
|
|
// Synopsis: Parse a CDbFirstRowsNode operator
|
|
//
|
|
// Arguments: [pTree] -- CDbFirstRowsNode node at root of tree to be parsed
|
|
//
|
|
// History: 19-Jun-2000 KitmanH Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CParseCommandTree::ParseFirstRowsNode( CDbCmdTreeNode *pTree )
|
|
{
|
|
CheckRecursionLimit();
|
|
|
|
CheckOperatorArity( pTree, 1 );
|
|
|
|
ULONG cFirstRows = ((CDbFirstRowsNode *)pTree)->GetValue();
|
|
if ( cFirstRows == 0 )
|
|
{
|
|
//
|
|
// A query with zero results is uninteresting
|
|
//
|
|
SetError( pTree );
|
|
}
|
|
|
|
//
|
|
// If a top node has already been encountered, then set the
|
|
// limit on results to the minimum of the two Top values
|
|
//
|
|
if ( _cFirstRows == 0 )
|
|
{
|
|
_cFirstRows = cFirstRows;
|
|
}
|
|
else
|
|
{
|
|
if ( cFirstRows < _cFirstRows )
|
|
_cFirstRows = cFirstRows;
|
|
}
|
|
|
|
CDbCmdTreeNode *pChild = pTree->GetFirstChild();
|
|
|
|
ParseTree( pChild );
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::SetError, private
|
|
//
|
|
// Synopsis: Mark an error in a command tree node.
|
|
//
|
|
// Arguments: [pNode] -- CDbCmdTreeNode node to check
|
|
// [scError] -- optional error code.
|
|
//
|
|
// Returns: doesn't. Throws the error set in the node.
|
|
//
|
|
// History: 31 May 95 AlanW Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void CParseCommandTree::SetError(
|
|
CDbCmdTreeNode* pNode,
|
|
SCODE scError)
|
|
{
|
|
vqDebugOut(( DEB_ERROR, "SetError - node %x error %x\n",
|
|
pNode, scError ));
|
|
pNode->SetError( scError );
|
|
if ( ! SUCCEEDED( _scError ))
|
|
{
|
|
_scError = scError;
|
|
_pErrorNode = pNode;
|
|
}
|
|
|
|
THROW( CException( DB_E_ERRORSINCOMMAND ));
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::CheckOperatorArity, private
|
|
//
|
|
// Synopsis: Verify that a tree node has the correct number of
|
|
// operands.
|
|
//
|
|
// Arguments: [pNode] -- CDbCmdTreeNode node to check
|
|
// [cOperands] -- number of operands expected. If
|
|
// cOperands is negative, at least -cOperands
|
|
// must be present. Otherwise, exactly cOperands
|
|
// must be present.
|
|
//
|
|
// Returns: unsigned - the number of operands found
|
|
//
|
|
// History: 31 May 95 AlanW Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
unsigned CParseCommandTree::CheckOperatorArity(
|
|
CDbCmdTreeNode* pNode,
|
|
int cOperands)
|
|
{
|
|
int cOps = 0;
|
|
|
|
for (CDbCmdTreeNode* pChild = pNode->GetFirstChild();
|
|
pChild;
|
|
pChild = pChild->GetNextSibling(), cOps++)
|
|
{
|
|
if (cOperands >= 0 && cOps > cOperands)
|
|
pChild->SetError( E_UNEXPECTED );
|
|
}
|
|
|
|
if (cOperands < 0)
|
|
{
|
|
//
|
|
// -(cOperands) or more operands are permitted
|
|
//
|
|
|
|
if (cOps < -(cOperands))
|
|
SetError(pNode, E_INVALIDARG);
|
|
}
|
|
else
|
|
{
|
|
if (cOps != cOperands)
|
|
SetError(pNode, E_INVALIDARG);
|
|
}
|
|
return (unsigned) cOps;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::GetColumnPropSpec, private
|
|
//
|
|
// Synopsis: Return a column identifier from a tree node argument
|
|
//
|
|
// Arguments: [pNode] -- CDbCmdTreeNode node to check
|
|
// [eKind] -- expected value kind
|
|
//
|
|
// Returns: PROPID from the pidmapper
|
|
//
|
|
// History: 12 Jun 95 AlanW Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
PROPID CParseCommandTree::GetColumnPropSpec(
|
|
CDbCmdTreeNode* pNode,
|
|
CFullPropSpec& Col)
|
|
{
|
|
if (pNode->GetValueType() != DBVALUEKIND_ID)
|
|
SetError(pNode, E_INVALIDARG);
|
|
|
|
CDbColumnNode * pColumnNode = (CDbColumnNode *)pNode;
|
|
|
|
if (pColumnNode->IsPropertyPropid())
|
|
{
|
|
// pids 0 and 1 are reserved
|
|
|
|
if ( ( PID_CODEPAGE == pColumnNode->GetPropertyPropid() ) ||
|
|
( PID_DICTIONARY == pColumnNode->GetPropertyPropid() ) )
|
|
SetError(pNode, E_INVALIDARG);
|
|
|
|
Col.SetProperty( pColumnNode->GetPropertyPropid() );
|
|
}
|
|
else /* (pColumnNode->IsPropertyName()) */
|
|
{
|
|
Col.SetProperty( pColumnNode->GetPropertyName() );
|
|
}
|
|
Col.SetPropSet( pColumnNode->GetPropSet() );
|
|
|
|
return _pidmap.NameToPid( Col );
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CParseCommandTree::GetValue, private
|
|
//
|
|
// Synopsis: Return a scalar constant from a tree node argument
|
|
//
|
|
// Arguments: [pNode] -- CDbCmdTreeNode node to check
|
|
//
|
|
// Returns: CStorageVariant& - reference to value of variant in node
|
|
//
|
|
// History: 12 Jun 95 AlanW Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL CParseCommandTree::GetValue (
|
|
CDbCmdTreeNode* pNode,
|
|
CStorageVariant & val )
|
|
{
|
|
((CDbScalarValue *)pNode)->Value( val );
|
|
return TRUE;
|
|
}
|