Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1080 lines
32 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1995 - 2000.
  5. //
  6. // File: Parstree.cxx
  7. //
  8. // Contents: Converts OLE-DB command tree into CColumns, CSort, CRestriction
  9. // and CCategorization.
  10. //
  11. // Classes: CParseCommandTree
  12. //
  13. // History: 31 May 95 AlanW Created
  14. //
  15. //--------------------------------------------------------------------------
  16. #include <pch.cxx>
  17. #pragma hdrstop
  18. #include <coldesc.hxx>
  19. #include <pidmap.hxx>
  20. #include <parstree.hxx>
  21. static GUID guidBmk = DBBMKGUID;
  22. const CFullPropSpec colChapter( guidBmk, PROPID_DBBMK_CHAPTER );
  23. const CFullPropSpec colBookmark( guidBmk, PROPID_DBBMK_BOOKMARK );
  24. //+---------------------------------------------------------------------------
  25. //
  26. // Method: CParseCommandTree::~CParseCommandTree, public
  27. //
  28. // Synopsis: Destructor of CParseCommandTree
  29. //
  30. //----------------------------------------------------------------------------
  31. CParseCommandTree::~CParseCommandTree( void )
  32. {
  33. if (_prst) delete _prst;
  34. }
  35. //--------------------------------------------------------------------------
  36. //
  37. // Command tree syntax accepted:
  38. //
  39. // QueryTree :
  40. // Table |
  41. // ContentTable |
  42. // SetUnion |
  43. // Categorization |
  44. // Projection |
  45. // OrderedQueryTree |
  46. // Restriction |
  47. // TopNode QueryTree
  48. // Categorization :
  49. // nesting OrderedQueryTree GroupingList ParentList ChildList coldef
  50. // ContentTable :
  51. // <empty> |
  52. // scope_list_anchor scope_list_element |
  53. // ScopeList scope_list_element
  54. // GroupingList : ProjectList
  55. // ParentList : ProjectList
  56. // ChildList : ProjectList
  57. // Projection :
  58. // ProjectOperator QueryTree ProjectList
  59. // ProjectOperator :
  60. // project | project_order_preserving
  61. // ProjectList :
  62. // project_list_anchor project_list_element |
  63. // ProjectList project_list_element
  64. // OrderedQueryTree :
  65. // sort QueryTree SortList
  66. // ScopeList :
  67. // scope_list_anchor scope_list_element |
  68. // ScopeList scope_list_element
  69. // SetUnion:
  70. // ContentTable ContentTable |
  71. // SetUnion ContentTable
  72. // SortList :
  73. // sort_list_anchor sort_list_element |
  74. // SortList sort_list_element
  75. // Restriction :
  76. // select QueryTree ExprTree |
  77. // select_order_preserving QueryTree ExprTree
  78. // Table :
  79. // scalar_identifier( Table )
  80. // ExprTree :
  81. // not ExprTree |
  82. // and ExprList |
  83. // or ExprList |
  84. // content_proximity ContentExprList
  85. // vector_or ContentExprList
  86. // ExprTerm
  87. // ExprList :
  88. // ExprTree ExprTree |
  89. // ExprList ExprTree
  90. // ExprTerm :
  91. // ContentExpr |
  92. // ValueExpr |
  93. // LikeExpr
  94. // ValueExpr :
  95. // ValueOperator scalar_identifier(ColId) scalar_constant
  96. // ValueOperator :
  97. // equal | not_equal | less | less_equal | greater | greater_equal |
  98. // equal_any | not_equal_any | less_any | less_equal_any |
  99. // greater_any | greater_equal_any |
  100. // equal_all | not_equal_all | less_all | less_equal_all |
  101. // greater_all | greater_equal_all | any_bits | all_bits
  102. // LikeExpr :
  103. // like(OfsRegexp) scalar_identifier(ColId) scalar_constant
  104. // ContentExprTree :
  105. // not ContentExprTree |
  106. // and ContentExprList |
  107. // or ContentExprList |
  108. // content_proximity ContentExprList
  109. // ContentExpr
  110. // ContentExprList :
  111. // ContentExprTree ContentExprTree |
  112. // ContentExprList ContentExprTree
  113. // ContentExpr :
  114. // content PhraseList |
  115. // freetext_content PhraseList
  116. //
  117. //+---------------------------------------------------------------------------
  118. //+---------------------------------------------------------------------------
  119. //
  120. // Method: CParseCommandTree::ParseTree, public
  121. //
  122. // Synopsis: Parse a CDbCmdTreeNode into projection, sort, restriction
  123. // and categorizations.
  124. //
  125. // Arguments: [pTree] -- CDbCmdTreeNode node at root of tree to be parsed
  126. //
  127. // Returns: nothing. Throws if error in tree.
  128. //
  129. // History: 31 May 95 AlanW Created
  130. //
  131. //----------------------------------------------------------------------------
  132. void CParseCommandTree::ParseTree( CDbCmdTreeNode * pTree )
  133. {
  134. CheckRecursionLimit();
  135. if (pTree == 0)
  136. {
  137. vqDebugOut(( DEB_WARN, "ParseTree - null tree\n"));
  138. THROW( CException( E_INVALIDARG ));
  139. }
  140. switch (pTree->GetCommandType())
  141. {
  142. case DBOP_project:
  143. case DBOP_project_order_preserving:
  144. ParseProjection( pTree );
  145. break;
  146. case DBOP_sort:
  147. ParseSort( pTree );
  148. break;
  149. case DBOP_select:
  150. // case DBOP_select_order_preserving:
  151. ParseRestriction( pTree );
  152. break;
  153. case DBOP_nesting:
  154. ParseCategorization( pTree );
  155. break;
  156. case DBOP_table_name:
  157. if ( _wcsicmp( ((CDbTableId *)pTree)->GetTableName(), DBTABLEID_NAME ))
  158. SetError( pTree );
  159. break;
  160. case DBOP_top:
  161. ParseTopNode( pTree );
  162. break;
  163. case DBOP_firstrows:
  164. ParseFirstRowsNode( pTree );
  165. break;
  166. case DBOP_content_table:
  167. ParseScope( pTree );
  168. break;
  169. case DBOP_set_union:
  170. ParseMultiScopes( pTree );
  171. break;
  172. default:
  173. vqDebugOut(( DEB_WARN, "ParseTree - unexpected operator %d\n", pTree->GetCommandType() ));
  174. SetError( pTree );
  175. break;
  176. }
  177. return;
  178. }
  179. //+---------------------------------------------------------------------------
  180. //
  181. // Method: CParseCommandTree::ParseScope, public
  182. //
  183. // Synopsis: Parse a CDbCmdTreeNode scope operator
  184. //
  185. // Arguments: [pTree] -- CDbCmdTreeNode scope specification node
  186. //
  187. // History: 09-15-98 danleg Created
  188. //
  189. // Notes: Here is a variation of a tree with scope nodes. This routine
  190. // gets a tree rooted either at a content_table node if a single
  191. // scope is specified, or a set_union node if there are multiple
  192. // scopes.
  193. //
  194. // proj
  195. // _________________/
  196. // /
  197. // select ___________________ LA-proj
  198. // _____________/ ____/
  199. // / /
  200. // set_union ________ content LE_proj ____ LE_proj
  201. // ____/ ____/ ___/ __/
  202. // / / / /
  203. // / column_name column_name column_name
  204. // /
  205. // set_union ________________ content_table
  206. // __/ __/
  207. // / /
  208. // / ...
  209. // content_table ____ content_table
  210. // ___/ ___/
  211. // / /
  212. // LA_scp LA_scp
  213. // __/ __/
  214. // / /
  215. // LE_scp ... LE_scp ___ LE_scp ___ ...
  216. //
  217. //----------------------------------------------------------------------------
  218. void CParseCommandTree::ParseScope( CDbCmdTreeNode * pTree )
  219. {
  220. CheckRecursionLimit();
  221. Win4Assert( DBOP_content_table == pTree->GetCommandType() );
  222. if ( 0 != pTree->GetFirstChild() )
  223. {
  224. //
  225. // qualified scope
  226. //
  227. ParseScopeListElements( pTree );
  228. }
  229. else
  230. {
  231. //
  232. // unqualified scope
  233. //
  234. _cScopes++;
  235. _xaMachines.SetSize( _cScopes );
  236. _xaScopes.SetSize( _cScopes );
  237. _xaFlags.SetSize( _cScopes );
  238. _xaCatalogs.SetSize( _cScopes );
  239. CDbContentTable * pCntntTbl =
  240. (CDbContentTable *) (pTree->CastToStruct())->value.pdbcntnttblValue;
  241. _xaMachines[_cScopes-1] = pCntntTbl->GetMachine();
  242. _xaCatalogs[_cScopes-1] = pCntntTbl->GetCatalog();
  243. _xaFlags[_cScopes-1] = QUERY_DEEP | QUERY_PHYSICAL_PATH;
  244. _xaScopes[_cScopes-1] = L"\\";
  245. }
  246. if ( 0 != pTree->GetNextSibling() &&
  247. DBOP_content_table == pTree->GetNextSibling()->GetCommandType() )
  248. ParseTree( pTree->GetNextSibling() );
  249. }
  250. //+---------------------------------------------------------------------------
  251. //
  252. // Method: CParseCommandTree::ParseScopeListElements, public
  253. //
  254. // Synopsis:
  255. //
  256. // Arguments: [pcntntTbl] -- node consisting of catalog/machine name info
  257. // [pTree] -- CDbCmdTreeNode scope specification node
  258. //
  259. // History: 01-24-99 danleg Created
  260. //
  261. //----------------------------------------------------------------------------
  262. void CParseCommandTree::ParseScopeListElements( CDbCmdTreeNode * pTree )
  263. {
  264. CheckRecursionLimit();
  265. if ( 0 == pTree->GetFirstChild() )
  266. {
  267. SetError( pTree );
  268. }
  269. if ( DBOP_scope_list_anchor != pTree->GetFirstChild()->GetCommandType() )
  270. {
  271. SetError( pTree->GetFirstChild() );
  272. }
  273. CheckOperatorArity( pTree->GetFirstChild(), -1 );
  274. CDbContentTable * pCntntTbl =
  275. (CDbContentTable *) (pTree->CastToStruct())->value.pdbcntnttblValue;
  276. CDbCmdTreeNode * pLE_SCP = pTree->GetFirstChild()->GetFirstChild();
  277. for ( ;
  278. pLE_SCP;
  279. pLE_SCP = pLE_SCP->GetNextSibling() )
  280. {
  281. if ( DBOP_scope_list_element != pLE_SCP->GetCommandType() ||
  282. 0 != pLE_SCP->GetFirstChild() )
  283. {
  284. SetError( pLE_SCP );
  285. }
  286. VerifyValueType( pLE_SCP, DBVALUEKIND_CONTENTSCOPE );
  287. CDbContentScope * pCntntScp =
  288. (CDbContentScope *) (pLE_SCP->CastToStruct())->value.pdbcntntscpValue;
  289. _cScopes++;
  290. _xaMachines.SetSize( _cScopes );
  291. _xaScopes.SetSize( _cScopes );
  292. _xaFlags.SetSize( _cScopes );
  293. _xaCatalogs.SetSize( _cScopes );
  294. _xaMachines[_cScopes-1] = pCntntTbl->GetMachine();
  295. _xaCatalogs[_cScopes-1] = pCntntTbl->GetCatalog();
  296. //
  297. // DBPROP_CI_SCOPE_FLAGS
  298. //
  299. if ( pCntntScp->GetType() & SCOPE_TYPE_WINPATH )
  300. _xaFlags[_cScopes-1] = QUERY_PHYSICAL_PATH;
  301. else if ( pCntntScp->GetType() & SCOPE_TYPE_VPATH )
  302. _xaFlags[_cScopes-1] = QUERY_VIRTUAL_PATH;
  303. else
  304. {
  305. // unknown flag
  306. SetError( pLE_SCP );
  307. }
  308. if ( pCntntScp->GetFlags() & SCOPE_FLAG_DEEP )
  309. _xaFlags[_cScopes-1] |= QUERY_DEEP;
  310. else
  311. _xaFlags[_cScopes-1] |= QUERY_SHALLOW;
  312. //
  313. // DBPROP_CI_INCLUDE_SCOPES
  314. //
  315. _xaScopes[_cScopes-1] = pCntntScp->GetValue();
  316. }
  317. }
  318. //+---------------------------------------------------------------------------
  319. //
  320. // Method: CParseCommandTree::ParseMultiScopes, public
  321. //
  322. // Synopsis:
  323. //
  324. // Arguments: [pcntntTbl] -- node consisting of catalog/machine name info
  325. // [pTree] -- CDbCmdTreeNode scope specification node
  326. //
  327. // History: 01-24-99 danleg Created
  328. //
  329. // Notes: Two possibilities:
  330. //
  331. // set_union ___ content
  332. // ______________/
  333. // /
  334. // content_table ___ content_table ___ ... ___ content_table
  335. //
  336. //
  337. // -- or --
  338. //
  339. // set_union ___ content
  340. // ___________/
  341. // /
  342. // set_union _____________________________ set_union
  343. // _____/ ______/
  344. // / /
  345. // content_table ___ content_table content_table ___ content_table
  346. //
  347. //----------------------------------------------------------------------------
  348. void CParseCommandTree::ParseMultiScopes( CDbCmdTreeNode * pTree )
  349. {
  350. CheckRecursionLimit();
  351. Win4Assert( DBOP_set_union == pTree->GetCommandType() );
  352. Win4Assert( 0 != pTree->GetFirstChild() &&
  353. 0 != pTree->GetNextSibling() );
  354. if ( DBOP_content_table == pTree->GetCommandType() )
  355. {
  356. CDbCmdTreeNode * pCntntTbl = pTree->GetFirstChild();
  357. for ( ;
  358. pCntntTbl;
  359. pCntntTbl = pCntntTbl->GetNextSibling() )
  360. {
  361. if ( DBOP_content_table != pCntntTbl->GetCommandType() )
  362. SetError( pCntntTbl );
  363. ParseScope( pCntntTbl );
  364. }
  365. }
  366. else
  367. {
  368. Win4Assert( DBOP_set_union == pTree->GetCommandType() );
  369. if ( DBOP_content_table != pTree->GetFirstChild()->GetCommandType() &&
  370. DBOP_set_union != pTree->GetFirstChild()->GetCommandType() )
  371. SetError( pTree );
  372. ParseTree( pTree->GetFirstChild() );
  373. //
  374. // Note that the DBOP_content branch gets parsed by ParseRestriction
  375. //
  376. if ( DBOP_content_table == pTree->GetNextSibling()->GetCommandType() ||
  377. DBOP_set_union == pTree->GetNextSibling()->GetCommandType() )
  378. ParseTree( pTree->GetNextSibling() );
  379. }
  380. }
  381. //+---------------------------------------------------------------------------
  382. //
  383. // Method: CParseCommandTree::ParseProjection, public
  384. //
  385. // Synopsis: Parse a CDbCmdTreeNode project operator
  386. //
  387. // Arguments: [pTree] -- CDbCmdTreeNode node at root of tree to be parsed
  388. //
  389. // History: 31 May 95 AlanW Created
  390. //
  391. //----------------------------------------------------------------------------
  392. void CParseCommandTree::ParseProjection( CDbCmdTreeNode * pTree )
  393. {
  394. CheckRecursionLimit();
  395. CheckOperatorArity( pTree, 2 );
  396. CDbCmdTreeNode * pChild = pTree->GetFirstChild();
  397. //
  398. // Parse the projection list first, so a projection higher in the
  399. // tree takes precedence. A projection is not permitted in a tree
  400. // that also contains a nesting node.
  401. //
  402. // Should a higher level projection take precedence?
  403. // We currently return an error.
  404. //
  405. if (_categ.Count() == 0 && _cols.Count() == 0)
  406. ParseProjectList ( pChild->GetNextSibling(), _cols );
  407. else
  408. SetError( pTree );
  409. ParseTree( pChild );
  410. }
  411. //+---------------------------------------------------------------------------
  412. //
  413. // Method: CParseCommandTree::ParseProjectList, public
  414. //
  415. // Synopsis: Parse a CDbCmdTreeNode project list
  416. //
  417. // Arguments: [pTree] -- CDbCmdTreeNode node for project list head
  418. // [Cols] -- CColumnSet in which columns are collected
  419. //
  420. // History: 31 May 95 AlanW Created
  421. //
  422. //----------------------------------------------------------------------------
  423. void CParseCommandTree::ParseProjectList( CDbCmdTreeNode *pTree, CColumnSet& Cols )
  424. {
  425. CheckRecursionLimit();
  426. if (pTree->GetCommandType() != DBOP_project_list_anchor ||
  427. pTree->GetFirstChild() == 0)
  428. {
  429. SetError( pTree );
  430. }
  431. CFullPropSpec Col;
  432. CDbProjectListElement* pList = (CDbProjectListElement *) pTree->GetFirstChild();
  433. for ( ;
  434. pList;
  435. pList = (CDbProjectListElement *)pList->GetNextSibling())
  436. {
  437. if ( pList->GetCommandType() != DBOP_project_list_element ||
  438. pList->GetFirstChild() == 0 )
  439. {
  440. SetError( pList );
  441. }
  442. CDbCmdTreeNode * pColumn = pList->GetFirstChild();
  443. if ( !pColumn->IsColumnName() )
  444. {
  445. SetError( pColumn );
  446. }
  447. // Add element to projection list
  448. PROPID pid = GetColumnPropSpec(pColumn, Col);
  449. Cols.Add( pid, Cols.Count() );
  450. if ( 0 != pList->GetName() )
  451. _pidmap.SetFriendlyName( pid, pList->GetName() );
  452. }
  453. }
  454. //+---------------------------------------------------------------------------
  455. //
  456. // Method: CParseCommandTree::ParseSort, public
  457. //
  458. // Synopsis: Parse a CDbCmdTreeNode sort node
  459. //
  460. // Arguments: [pTree] -- CDbCmdTreeNode node for sort list head
  461. //
  462. // Notes: Sort nodes are added to the CSortSet in private data
  463. //
  464. // History: 31 May 95 AlanW Created
  465. //
  466. //----------------------------------------------------------------------------
  467. void CParseCommandTree::ParseSort( CDbCmdTreeNode * pTree )
  468. {
  469. CheckRecursionLimit();
  470. CheckOperatorArity( pTree, 2 );
  471. CDbCmdTreeNode * pChild = pTree->GetFirstChild();
  472. //
  473. // Parse the sort list first, so a sort higher in the
  474. // tree is primary.
  475. //
  476. ParseSortList ( pChild->GetNextSibling() );
  477. ParseTree( pChild );
  478. }
  479. //+---------------------------------------------------------------------------
  480. //
  481. // Method: CParseCommandTree::ParseSortList, public
  482. //
  483. // Synopsis: Parse a CDbCmdTreeNode sort list
  484. //
  485. // Arguments: [pTree] -- CDbCmdTreeNode node for sort list head
  486. //
  487. // Notes: Sort nodes are added to the CSortSet in private data
  488. //
  489. // History: 31 May 95 AlanW Created
  490. //
  491. //----------------------------------------------------------------------------
  492. void CParseCommandTree::ParseSortList( CDbCmdTreeNode * pTree )
  493. {
  494. CheckRecursionLimit();
  495. if (pTree->GetCommandType() != DBOP_sort_list_anchor ||
  496. pTree->GetFirstChild() == 0)
  497. {
  498. SetError( pTree );
  499. }
  500. CDbSortListAnchor *pSortAnchor = (CDbSortListAnchor *) pTree;
  501. unsigned i = 0;
  502. for (CDbCmdTreeNode * pSortList = pSortAnchor->GetFirstChild();
  503. pSortList;
  504. pSortList = pSortList->GetNextSibling(), i++)
  505. {
  506. if (pSortList->GetCommandType() != DBOP_sort_list_element)
  507. {
  508. SetError( pSortList );
  509. }
  510. VerifyValueType( pSortList, DBVALUEKIND_SORTINFO );
  511. CheckOperatorArity( pSortList, 1 );
  512. CDbSortListElement* pSLE = (CDbSortListElement *) pSortList;
  513. CFullPropSpec Col;
  514. PROPID pid = GetColumnPropSpec(pSLE->GetFirstChild(), Col);
  515. DWORD dwOrder = pSLE->GetDirection() ? QUERY_SORTDESCEND :
  516. QUERY_SORTASCEND;
  517. LCID locale = pSLE->GetLocale();
  518. if ( _categ.Count() != 0 && i < _sort.Count() )
  519. {
  520. //
  521. // Check that the sort specification matches any set as a result of
  522. // the categorization (but which may differ in sort direction and
  523. // locale).
  524. //
  525. SSortKey & sortkey = _sort.Get(i);
  526. if (sortkey.pidColumn != pid)
  527. {
  528. SetError(pSLE);
  529. }
  530. if (sortkey.dwOrder != dwOrder)
  531. sortkey.dwOrder = dwOrder;
  532. if (sortkey.locale != locale)
  533. sortkey.locale = locale;
  534. }
  535. else
  536. {
  537. // Add element to the sort list
  538. SSortKey sortkey(pid, dwOrder, locale);
  539. _sort.Add( sortkey, _sort.Count() );
  540. }
  541. }
  542. }
  543. //+---------------------------------------------------------------------------
  544. //
  545. // Method: CParseCommandTree::ParseRestriction, public
  546. //
  547. // Synopsis: Parse a CDbCmdTreeNode select node
  548. //
  549. // Arguments: [pTree] -- CDbCmdTreeNode node for select tree
  550. //
  551. // History: 31 May 95 AlanW Created
  552. //
  553. //----------------------------------------------------------------------------
  554. void CParseCommandTree::ParseRestriction( CDbCmdTreeNode *pTree )
  555. {
  556. CheckRecursionLimit();
  557. //
  558. // Only one selection can exist in the tree; there must be exactly
  559. // two operands: the selection expression and a table identifier.
  560. // For use with AddPostProcessing, another select should be
  561. // allowed higher in the tree, with the restrictions anded
  562. // together.
  563. //
  564. if (_prst != 0 )
  565. SetError( pTree );
  566. CheckOperatorArity( pTree, 2 );
  567. VerifyValueType( pTree, DBVALUEKIND_EMPTY );
  568. CDbCmdTreeNode * pTable = pTree->GetFirstChild();
  569. CDbCmdTreeNode * pExpr = pTable->GetNextSibling();
  570. _prst = ParseExpression( pExpr );
  571. ParseTree( pTable );
  572. }
  573. //+---------------------------------------------------------------------------
  574. //
  575. // Method: CParseCommandTree::ParseCategorization, public
  576. //
  577. // Synopsis: Parse a CDbCmdTreeNode nesting node
  578. //
  579. // Arguments: [pTree] -- CDbCmdTreeNode node for nesting tree
  580. //
  581. // History: 31 Jul 1995 AlanW Created
  582. //
  583. // Notes: Syntax accepted:
  584. //
  585. // Categorization :
  586. // nesting OrderedQueryTree GroupingList
  587. // ParentList ChildList coldef
  588. // GroupingList : ProjectList
  589. // ParentList : ProjectList
  590. // ChildList : ProjectList
  591. //
  592. // The GroupingList may be a list of columns on which
  593. // to do a unique value categorization, or a defined-by-guid
  594. // function that specifies one of the other categorizations.
  595. // The ParentList may only specify columns in the GroupingList,
  596. // columns in upper level groupings, or certain aggregations
  597. // on those columns.
  598. // The ChildList forms the projectlist for the base table.
  599. // The coldef node for the nesting column can give only the
  600. // special column id for the chapter column.
  601. //
  602. // Only the unique value categorization is implemented at present.
  603. //
  604. //----------------------------------------------------------------------------
  605. void CParseCommandTree::ParseCategorization( CDbCmdTreeNode *pTree )
  606. {
  607. CheckRecursionLimit();
  608. CheckOperatorArity( pTree, 5 );
  609. VerifyValueType( pTree, DBVALUEKIND_EMPTY );
  610. //
  611. // We can't sort or project the output of a categorized table. We can,
  612. // however permit a sort as a side-effect of an existing
  613. // nesting node for a unique value categorization.
  614. //
  615. if (_categ.Count() == 0 &&
  616. (_sort.Count() > 0 || _cols.Count() > 0))
  617. SetError( pTree, E_INVALIDARG );
  618. Win4Assert(_sort.Count() == _categ.Count());
  619. CDbCmdTreeNode * pTable = pTree->GetFirstChild();
  620. CDbCmdTreeNode * pGroupingList = pTable->GetNextSibling();
  621. CDbCmdTreeNode * pParentList = pGroupingList->GetNextSibling();
  622. CDbCmdTreeNode * pChildList = pParentList->GetNextSibling();
  623. CDbCmdTreeNode * pNestingColumn = pChildList->GetNextSibling();
  624. CheckOperatorArity( pGroupingList, 1 ); // one grouping col. now
  625. // CheckOperatorArity( pParentList, -1 );
  626. // CheckOperatorArity( pChildList, -1 );
  627. CheckOperatorArity( pNestingColumn, 0 );
  628. //
  629. // For now, the only supported categorization is a unique
  630. // value categorization. For this, the grouping list is
  631. // just a projection list that gives the unique columns.
  632. //
  633. CColumnSet colGroup;
  634. ParseProjectList( pGroupingList, colGroup );
  635. for (unsigned i = 0; i < colGroup.Count(); i++)
  636. {
  637. SSortKey sortkey(colGroup.Get(i), QUERY_SORTASCEND);
  638. _sort.Add( sortkey, _sort.Count() );
  639. }
  640. //
  641. // For now, the parent list can only mention columns also
  642. // in the grouping list (which are now in the sort list).
  643. // In addition, the bookmark and chapter columns will be
  644. // available in the parent table.
  645. // We should also one day allow some aggregations.
  646. //
  647. CColumnSet colParent;
  648. if (pParentList->GetFirstChild() != 0)
  649. {
  650. ParseProjectList( pParentList, colParent );
  651. if (colParent.Count() > _sort.Count() + 2)
  652. {
  653. SetError( pParentList, E_INVALIDARG );
  654. }
  655. }
  656. //
  657. // Check that the columns are valid
  658. //
  659. pParentList = pParentList->GetFirstChild();
  660. BOOL fChapterFound = FALSE;
  661. for (i = 0; i < colParent.Count(); i++)
  662. {
  663. CFullPropSpec const * pCol = _pidmap.PidToName(colParent.Get(i));
  664. if (*pCol == colChapter)
  665. {
  666. fChapterFound = TRUE;
  667. }
  668. else if (*pCol == colBookmark)
  669. {
  670. // Bookmark is permitted in any parent column list
  671. ;
  672. }
  673. else
  674. {
  675. BOOL fFound = FALSE;
  676. for (unsigned j = 0; j < _sort.Count(); j++)
  677. if ( colParent.Get(i) == _sort.Get(j).pidColumn )
  678. {
  679. fFound = TRUE;
  680. break;
  681. }
  682. if (!fFound)
  683. SetError(pParentList, E_INVALIDARG);
  684. }
  685. pParentList = pParentList->GetNextSibling();
  686. }
  687. if (! fChapterFound)
  688. colParent.Add( _pidmap.NameToPid(colChapter), colParent.Count());
  689. //
  690. // Columns in the child list replace any existing project list
  691. // (which can only have been set by a higher-level nesting node).
  692. //
  693. if (_cols.Count())
  694. {
  695. vqDebugOut(( DEB_WARN, "CParseCommandTree - "
  696. "child list in multi-nested command tree ignored\n" ));
  697. _cols.Clear();
  698. }
  699. if ( 0 != pChildList->GetFirstChild() )
  700. ParseProjectList( pChildList, _cols );
  701. XPtr<CCategSpec> pCatParam = new CUniqueCategSpec( );
  702. XPtr<CCategorizationSpec> pCat =
  703. new CCategorizationSpec( pCatParam.GetPointer(),
  704. colParent.Count() );
  705. pCatParam.Acquire();
  706. for (i = 0; i < colParent.Count(); i++)
  707. {
  708. pCat->SetColumn( colParent.Get(i), i );
  709. }
  710. _categ.Add( pCat.GetPointer(), _categ.Count() );
  711. pCat.Acquire();
  712. //
  713. // Now parse the rest of the tree, and check that _cols was set
  714. // either here or by a lower level nesting node.
  715. //
  716. ParseTree( pTable );
  717. if ( 0 == _cols.Count() )
  718. {
  719. Win4Assert( 0 == pChildList->GetFirstChild() );
  720. SetError( pChildList );
  721. }
  722. }
  723. //+---------------------------------------------------------------------------
  724. //
  725. // Method: CParseCommandTree::ParseTopNode, public
  726. //
  727. // Synopsis: Parse a CDbTopNode operator
  728. //
  729. // Arguments: [pTree] -- CDbTopNode node at root of tree to be parsed
  730. //
  731. // History: 21 Feb 96 AlanW Created
  732. //
  733. //----------------------------------------------------------------------------
  734. void CParseCommandTree::ParseTopNode( CDbCmdTreeNode *pTree )
  735. {
  736. CheckRecursionLimit();
  737. CheckOperatorArity( pTree, 1 );
  738. ULONG cMaxResults = ((CDbTopNode *)pTree)->GetValue();
  739. if ( cMaxResults == 0 )
  740. {
  741. //
  742. // A query with zero results is uninteresting
  743. //
  744. SetError( pTree );
  745. }
  746. //
  747. // If a top node has already been encountered, then set the
  748. // limit on results to the minimum of the two Top values
  749. //
  750. if ( _cMaxResults == 0 )
  751. {
  752. _cMaxResults = cMaxResults;
  753. }
  754. else
  755. {
  756. if ( cMaxResults < _cMaxResults )
  757. _cMaxResults = cMaxResults;
  758. }
  759. CDbCmdTreeNode *pChild = pTree->GetFirstChild();
  760. ParseTree( pChild );
  761. }
  762. //+---------------------------------------------------------------------------
  763. //
  764. // Method: CParseCommandTree::ParseFirstRowsNode, public
  765. //
  766. // Synopsis: Parse a CDbFirstRowsNode operator
  767. //
  768. // Arguments: [pTree] -- CDbFirstRowsNode node at root of tree to be parsed
  769. //
  770. // History: 19-Jun-2000 KitmanH Created
  771. //
  772. //----------------------------------------------------------------------------
  773. void CParseCommandTree::ParseFirstRowsNode( CDbCmdTreeNode *pTree )
  774. {
  775. CheckRecursionLimit();
  776. CheckOperatorArity( pTree, 1 );
  777. ULONG cFirstRows = ((CDbFirstRowsNode *)pTree)->GetValue();
  778. if ( cFirstRows == 0 )
  779. {
  780. //
  781. // A query with zero results is uninteresting
  782. //
  783. SetError( pTree );
  784. }
  785. //
  786. // If a top node has already been encountered, then set the
  787. // limit on results to the minimum of the two Top values
  788. //
  789. if ( _cFirstRows == 0 )
  790. {
  791. _cFirstRows = cFirstRows;
  792. }
  793. else
  794. {
  795. if ( cFirstRows < _cFirstRows )
  796. _cFirstRows = cFirstRows;
  797. }
  798. CDbCmdTreeNode *pChild = pTree->GetFirstChild();
  799. ParseTree( pChild );
  800. }
  801. //+---------------------------------------------------------------------------
  802. //
  803. // Method: CParseCommandTree::SetError, private
  804. //
  805. // Synopsis: Mark an error in a command tree node.
  806. //
  807. // Arguments: [pNode] -- CDbCmdTreeNode node to check
  808. // [scError] -- optional error code.
  809. //
  810. // Returns: doesn't. Throws the error set in the node.
  811. //
  812. // History: 31 May 95 AlanW Created
  813. //
  814. //----------------------------------------------------------------------------
  815. void CParseCommandTree::SetError(
  816. CDbCmdTreeNode* pNode,
  817. SCODE scError)
  818. {
  819. vqDebugOut(( DEB_ERROR, "SetError - node %x error %x\n",
  820. pNode, scError ));
  821. pNode->SetError( scError );
  822. if ( ! SUCCEEDED( _scError ))
  823. {
  824. _scError = scError;
  825. _pErrorNode = pNode;
  826. }
  827. THROW( CException( DB_E_ERRORSINCOMMAND ));
  828. }
  829. //+---------------------------------------------------------------------------
  830. //
  831. // Method: CParseCommandTree::CheckOperatorArity, private
  832. //
  833. // Synopsis: Verify that a tree node has the correct number of
  834. // operands.
  835. //
  836. // Arguments: [pNode] -- CDbCmdTreeNode node to check
  837. // [cOperands] -- number of operands expected. If
  838. // cOperands is negative, at least -cOperands
  839. // must be present. Otherwise, exactly cOperands
  840. // must be present.
  841. //
  842. // Returns: unsigned - the number of operands found
  843. //
  844. // History: 31 May 95 AlanW Created
  845. //
  846. //----------------------------------------------------------------------------
  847. unsigned CParseCommandTree::CheckOperatorArity(
  848. CDbCmdTreeNode* pNode,
  849. int cOperands)
  850. {
  851. int cOps = 0;
  852. for (CDbCmdTreeNode* pChild = pNode->GetFirstChild();
  853. pChild;
  854. pChild = pChild->GetNextSibling(), cOps++)
  855. {
  856. if (cOperands >= 0 && cOps > cOperands)
  857. pChild->SetError( E_UNEXPECTED );
  858. }
  859. if (cOperands < 0)
  860. {
  861. //
  862. // -(cOperands) or more operands are permitted
  863. //
  864. if (cOps < -(cOperands))
  865. SetError(pNode, E_INVALIDARG);
  866. }
  867. else
  868. {
  869. if (cOps != cOperands)
  870. SetError(pNode, E_INVALIDARG);
  871. }
  872. return (unsigned) cOps;
  873. }
  874. //+---------------------------------------------------------------------------
  875. //
  876. // Method: CParseCommandTree::GetColumnPropSpec, private
  877. //
  878. // Synopsis: Return a column identifier from a tree node argument
  879. //
  880. // Arguments: [pNode] -- CDbCmdTreeNode node to check
  881. // [eKind] -- expected value kind
  882. //
  883. // Returns: PROPID from the pidmapper
  884. //
  885. // History: 12 Jun 95 AlanW Created
  886. //
  887. //----------------------------------------------------------------------------
  888. PROPID CParseCommandTree::GetColumnPropSpec(
  889. CDbCmdTreeNode* pNode,
  890. CFullPropSpec& Col)
  891. {
  892. if (pNode->GetValueType() != DBVALUEKIND_ID)
  893. SetError(pNode, E_INVALIDARG);
  894. CDbColumnNode * pColumnNode = (CDbColumnNode *)pNode;
  895. if (pColumnNode->IsPropertyPropid())
  896. {
  897. // pids 0 and 1 are reserved
  898. if ( ( PID_CODEPAGE == pColumnNode->GetPropertyPropid() ) ||
  899. ( PID_DICTIONARY == pColumnNode->GetPropertyPropid() ) )
  900. SetError(pNode, E_INVALIDARG);
  901. Col.SetProperty( pColumnNode->GetPropertyPropid() );
  902. }
  903. else /* (pColumnNode->IsPropertyName()) */
  904. {
  905. Col.SetProperty( pColumnNode->GetPropertyName() );
  906. }
  907. Col.SetPropSet( pColumnNode->GetPropSet() );
  908. return _pidmap.NameToPid( Col );
  909. }
  910. //+---------------------------------------------------------------------------
  911. //
  912. // Method: CParseCommandTree::GetValue, private
  913. //
  914. // Synopsis: Return a scalar constant from a tree node argument
  915. //
  916. // Arguments: [pNode] -- CDbCmdTreeNode node to check
  917. //
  918. // Returns: CStorageVariant& - reference to value of variant in node
  919. //
  920. // History: 12 Jun 95 AlanW Created
  921. //
  922. //----------------------------------------------------------------------------
  923. BOOL CParseCommandTree::GetValue (
  924. CDbCmdTreeNode* pNode,
  925. CStorageVariant & val )
  926. {
  927. ((CDbScalarValue *)pNode)->Value( val );
  928. return TRUE;
  929. }