Leaked source code of windows server 2003
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.

1341 lines
38 KiB

  1. /*++
  2. Copyright (C) 1997-2001 Microsoft Corporation
  3. Module Name:
  4. WQLSCAN.CPP
  5. Abstract:
  6. WQL Prefix Scanner
  7. This module implements a specially cased shift-reduce parser to
  8. parse out selected columns, JOINed tables and aliases, while ignoring
  9. the rest of the query.
  10. History:
  11. raymcc 17-Oct-97 SMS extensions.
  12. --*/
  13. #include "precomp.h"
  14. #include <stdio.h>
  15. #include <flexarry.h>
  16. #include <wqllex.h>
  17. #include <wqlnode.h>
  18. #include <wqlscan.h>
  19. inline wchar_t *Macro_CloneLPWSTR(LPCWSTR src)
  20. {
  21. if (!src)
  22. return 0;
  23. size_t length = wcslen(src) + 1;
  24. wchar_t *dest = new wchar_t[length];
  25. if (!dest)
  26. return 0;
  27. memcpy(dest, src, length * sizeof wchar_t);
  28. return dest;
  29. }
  30. #define trace(x) printf x
  31. class CTokenArray : public CFlexArray
  32. {
  33. public:
  34. ~CTokenArray() { Empty(); }
  35. void Empty()
  36. {
  37. for (int i = 0; i < Size(); i++) delete PWSLexToken(GetAt(i));
  38. CFlexArray::Empty();
  39. }
  40. };
  41. //***************************************************************************
  42. //
  43. // CWQLScanner::CWQLScanner
  44. //
  45. // Constructor
  46. //
  47. // Parameters:
  48. // <pSrc> A source from which to lex from.
  49. //
  50. //***************************************************************************
  51. CWQLScanner::CWQLScanner(CGenLexSource *pSrc)
  52. {
  53. m_pLexer = new CGenLexer(WQL_LexTable, pSrc);
  54. if (m_pLexer == 0 || GetLastError() == ERROR_NOT_ENOUGH_MEMORY)
  55. {
  56. delete m_pLexer;
  57. throw CX_MemoryException();
  58. }
  59. m_nLine = 0;
  60. m_pTokenText = 0;
  61. m_nCurrentToken = 0;
  62. m_bCount = FALSE;
  63. }
  64. //***************************************************************************
  65. //
  66. // CWQLScanner::~CWQLScanner
  67. //
  68. //***************************************************************************
  69. CWQLScanner::~CWQLScanner()
  70. {
  71. delete m_pLexer;
  72. ClearTokens();
  73. ClearTableRefs();
  74. ClearPropRefs();
  75. }
  76. //***************************************************************************
  77. //
  78. //***************************************************************************
  79. BOOL CWQLScanner::GetReferencedAliases(CWStringArray &aAliases)
  80. {
  81. for (int i = 0; i < m_aTableRefs.Size(); i++)
  82. {
  83. WSTableRef *pTRef = (WSTableRef *) m_aTableRefs[i];
  84. aAliases.Add(pTRef->m_pszAlias);
  85. }
  86. return TRUE;
  87. }
  88. //***************************************************************************
  89. //
  90. //***************************************************************************
  91. BOOL CWQLScanner::GetReferencedTables(CWStringArray &aClasses)
  92. {
  93. for (int i = 0; i < m_aTableRefs.Size(); i++)
  94. {
  95. WSTableRef *pTRef = (WSTableRef *) m_aTableRefs[i];
  96. aClasses.Add(pTRef->m_pszTable);
  97. }
  98. return TRUE;
  99. }
  100. //***************************************************************************
  101. //
  102. //***************************************************************************
  103. void CWQLScanner::ClearTokens()
  104. {
  105. for (int i = 0; i < m_aTokens.Size(); i++)
  106. delete (WSLexToken *) m_aTokens[i];
  107. }
  108. //***************************************************************************
  109. //
  110. //***************************************************************************
  111. void CWQLScanner::ClearPropRefs()
  112. {
  113. for (int i = 0; i < m_aPropRefs.Size(); i++)
  114. delete (SWQLColRef *) m_aPropRefs[i];
  115. }
  116. //***************************************************************************
  117. //
  118. //***************************************************************************
  119. void CWQLScanner::ClearTableRefs()
  120. {
  121. for (int i = 0; i < m_aTableRefs.Size(); i++)
  122. delete (WSTableRef *) m_aTableRefs[i];
  123. m_aTableRefs.Empty();
  124. }
  125. //***************************************************************************
  126. //
  127. // Next()
  128. //
  129. // Advances to the next token and recognizes keywords, etc.
  130. //
  131. //***************************************************************************
  132. struct WqlKeyword
  133. {
  134. LPWSTR m_pKeyword;
  135. int m_nTokenCode;
  136. };
  137. static WqlKeyword KeyWords[] = // Keep this alphabetized for binary search
  138. {
  139. L"ALL", WQL_TOK_ALL,
  140. L"AND", WQL_TOK_AND,
  141. L"AS", WQL_TOK_AS,
  142. L"BETWEEN", WQL_TOK_BETWEEN,
  143. L"BY", WQL_TOK_BY,
  144. L"COUNT", WQL_TOK_COUNT,
  145. L"DATEPART", WQL_TOK_DATEPART,
  146. L"DISTINCT", WQL_TOK_DISTINCT,
  147. L"FIRSTROW", WQL_TOK_FIRSTROW,
  148. L"FROM", WQL_TOK_FROM,
  149. L"FULL", WQL_TOK_FULL,
  150. L"GROUP", WQL_TOK_GROUP,
  151. L"HAVING", WQL_TOK_HAVING,
  152. L"IN", WQL_TOK_IN,
  153. L"INNER", WQL_TOK_INNER,
  154. L"IS", WQL_TOK_IS,
  155. L"ISA", WQL_TOK_ISA,
  156. L"ISNULL", WQL_TOK_ISNULL,
  157. L"JOIN", WQL_TOK_JOIN,
  158. L"LEFT", WQL_TOK_LEFT,
  159. L"LIKE", WQL_TOK_LIKE,
  160. L"LOWER", WQL_TOK_LOWER,
  161. L"NOT", WQL_TOK_NOT,
  162. L"NULL", WQL_TOK_NULL,
  163. L"ON", WQL_TOK_ON,
  164. L"OR", WQL_TOK_OR,
  165. L"ORDER", WQL_TOK_ORDER,
  166. L"OUTER", WQL_TOK_OUTER,
  167. L"QUALIFIER", WQL_TOK_QUALIFIER,
  168. L"RIGHT", WQL_TOK_RIGHT,
  169. L"SELECT", WQL_TOK_SELECT,
  170. L"UNION", WQL_TOK_UNION,
  171. L"UPPER", WQL_TOK_UPPER,
  172. L"WHERE", WQL_TOK_WHERE
  173. };
  174. const int NumKeywords = sizeof(KeyWords)/sizeof(WqlKeyword);
  175. BOOL CWQLScanner::Next()
  176. {
  177. if (!m_pLexer)
  178. return FALSE;
  179. m_nCurrentToken = m_pLexer->NextToken();
  180. if (m_nCurrentToken == WQL_TOK_ERROR)
  181. return FALSE;
  182. m_nLine = m_pLexer->GetLineNum();
  183. m_pTokenText = m_pLexer->GetTokenText();
  184. if (m_nCurrentToken == WQL_TOK_EOF)
  185. m_pTokenText = L"<end of file>";
  186. // Keyword check. Do a binary search
  187. // on the keyword table.
  188. // =================================
  189. if (m_nCurrentToken == WQL_TOK_IDENT)
  190. {
  191. int l = 0, u = NumKeywords - 1;
  192. while (l <= u)
  193. {
  194. int m = (l + u) / 2;
  195. if (wbem_wcsicmp(m_pTokenText, KeyWords[m].m_pKeyword) < 0)
  196. u = m - 1;
  197. else if (wbem_wcsicmp(m_pTokenText, KeyWords[m].m_pKeyword) > 0)
  198. l = m + 1;
  199. else // Match
  200. {
  201. m_nCurrentToken = KeyWords[m].m_nTokenCode;
  202. break;
  203. }
  204. }
  205. }
  206. return TRUE;
  207. }
  208. //***************************************************************************
  209. //
  210. // CWQLScanner::ExtractNext
  211. //
  212. //***************************************************************************
  213. PWSLexToken CWQLScanner::ExtractNext(BOOL bRemove)
  214. {
  215. if (m_aTokens.Size() == 0)
  216. return NULL;
  217. PWSLexToken pTok = PWSLexToken(m_aTokens[0]);
  218. if (bRemove)
  219. m_aTokens.RemoveAt(0);
  220. return pTok;
  221. }
  222. //***************************************************************************
  223. //
  224. // CWQLScanner::Pushback
  225. //
  226. //***************************************************************************
  227. int CWQLScanner::Pushback(PWSLexToken pPushbackTok)
  228. {
  229. return m_aTokens.InsertAt(0, pPushbackTok);
  230. }
  231. //***************************************************************************
  232. //
  233. // Shift-reduce parser entry.
  234. //
  235. //***************************************************************************
  236. int CWQLScanner::Parse()
  237. {
  238. int nRes = SYNTAX_ERROR;
  239. if (m_pLexer == NULL)
  240. return FAILED;
  241. m_pLexer->Reset();
  242. if (!Next())
  243. return LEXICAL_ERROR;
  244. // Completely tokenize the entire query and build a parse-stack.
  245. // =============================================================
  246. if (m_nCurrentToken == WQL_TOK_SELECT)
  247. {
  248. while (1)
  249. {
  250. WSLexToken *pTok = new WSLexToken;
  251. if (!pTok)
  252. return FAILED;
  253. pTok->m_nToken = m_nCurrentToken;
  254. pTok->m_pszTokenText = Macro_CloneLPWSTR(m_pTokenText);
  255. if (!pTok->m_pszTokenText)
  256. return FAILED;
  257. if (m_aTokens.Add(pTok))
  258. {
  259. delete pTok;
  260. return FAILED;
  261. }
  262. if (m_nCurrentToken == WQL_TOK_EOF)
  263. break;
  264. if (!Next())
  265. return LEXICAL_ERROR;
  266. }
  267. }
  268. else
  269. return SYNTAX_ERROR;
  270. // Reduce by extracting the select type keywords if possible.
  271. // ==========================================================
  272. nRes = ExtractSelectType();
  273. if (nRes)
  274. return nRes;
  275. // Eliminate all tokens from WHERE onwards.
  276. // ========================================
  277. StripWhereClause();
  278. // Reduce by extracting the select list.
  279. // =====================================
  280. if (!m_bCount)
  281. {
  282. nRes = SelectList();
  283. if (nRes != 0)
  284. return nRes;
  285. }
  286. else
  287. {
  288. // Strip everything until the FROM keyword is encountered.
  289. // =======================================================
  290. WSLexToken *pTok = ExtractNext(FALSE);
  291. if (pTok->m_nToken != WQL_TOK_OPEN_PAREN)
  292. {
  293. nRes = SelectList();
  294. if (nRes )
  295. return nRes;
  296. }
  297. else
  298. {
  299. pTok = ExtractNext();
  300. while (pTok)
  301. {
  302. if (pTok->m_nToken == WQL_TOK_FROM)
  303. {
  304. if (Pushback(pTok))
  305. {
  306. delete pTok;
  307. return FAILED;
  308. }
  309. break;
  310. }
  311. // Bug #46728: the count(*) clause
  312. // can be the only element of the select clause.
  313. else if (!wcscmp(pTok->m_pszTokenText, L","))
  314. {
  315. delete pTok;
  316. return SYNTAX_ERROR;
  317. }
  318. delete pTok;
  319. pTok = ExtractNext();
  320. }
  321. if (pTok == 0)
  322. return SYNTAX_ERROR;
  323. }
  324. }
  325. // Extract tables/aliases from JOIN clauses.
  326. // =========================================
  327. if (ReduceSql89Joins() != TRUE)
  328. {
  329. ClearTableRefs();
  330. if (ReduceSql92Joins() != TRUE)
  331. return SYNTAX_ERROR;
  332. }
  333. // Post process select clause to determine if
  334. // columns are tables or aliases.
  335. // ==========================================
  336. for (int i = 0; i < m_aPropRefs.Size(); i++)
  337. {
  338. SWQLColRef *pCRef = (SWQLColRef *) m_aPropRefs[i];
  339. if (pCRef->m_pTableRef != 0)
  340. {
  341. LPWSTR pTbl = AliasToTable(pCRef->m_pTableRef);
  342. if (pTbl == 0)
  343. continue;
  344. if (wbem_wcsicmp(pTbl, pCRef->m_pTableRef) == 0)
  345. pCRef->m_dwFlags |= WQL_FLAG_TABLE;
  346. else
  347. pCRef->m_dwFlags |= WQL_FLAG_ALIAS;
  348. }
  349. }
  350. if (m_aTableRefs.Size() == 0)
  351. return SYNTAX_ERROR;
  352. return SUCCESS;
  353. }
  354. //***************************************************************************
  355. //
  356. // CWQLScanner::StripWhereClause
  357. //
  358. // If present, removes the WHERE or ORDER BY clause. Because
  359. // of SQL Syntax, stripping the first of {ORDER BY, WHERE} will automatically
  360. // get rid of the other.
  361. //
  362. //***************************************************************************
  363. BOOL CWQLScanner::StripWhereClause()
  364. {
  365. for (int i = 0; i < m_aTokens.Size(); i++)
  366. {
  367. WSLexToken *pCurrent = (WSLexToken *) m_aTokens[i];
  368. // If a WHERE token is found, we have something to strip.
  369. // ======================================================
  370. if (pCurrent->m_nToken == WQL_TOK_WHERE ||
  371. pCurrent->m_nToken == WQL_TOK_ORDER)
  372. {
  373. int nNumTokensToRemove = m_aTokens.Size() - i - 1;
  374. for (int i2 = 0; i2 < nNumTokensToRemove; i2++)
  375. {
  376. delete PWSLexToken(m_aTokens[i]);
  377. m_aTokens.RemoveAt(i);
  378. }
  379. return TRUE;
  380. }
  381. }
  382. return FALSE;
  383. }
  384. //***************************************************************************
  385. //
  386. // CWQLScanner::ExtractSelectType
  387. //
  388. // Examines the prefix to reduce the query by eliminating the SELECT
  389. // and select-type keywords, such as ALL, DISTINCT, FIRSTROW, COUNT
  390. //
  391. // If COUNT is used, move past the open-close parentheses.
  392. //
  393. //***************************************************************************
  394. int CWQLScanner::ExtractSelectType()
  395. {
  396. // Verify that SELECT is the first token.
  397. // ======================================
  398. WSLexToken *pFront = ExtractNext();
  399. if (pFront == 0 || pFront->m_nToken == WQL_TOK_EOF)
  400. {
  401. delete pFront;
  402. return SYNTAX_ERROR;
  403. }
  404. if (pFront->m_nToken != WQL_TOK_SELECT)
  405. {
  406. delete pFront;
  407. return SYNTAX_ERROR;
  408. }
  409. delete pFront;
  410. // Check for possible select-type and extract it.
  411. // ==============================================
  412. pFront = ExtractNext();
  413. if (pFront == 0)
  414. return SYNTAX_ERROR;
  415. if (pFront->m_nToken == WQL_TOK_COUNT)
  416. {
  417. delete pFront;
  418. m_bCount = TRUE;
  419. }
  420. else if (pFront->m_nToken == WQL_TOK_ALL ||
  421. pFront->m_nToken == WQL_TOK_DISTINCT ||
  422. pFront->m_nToken == WQL_TOK_FIRSTROW
  423. )
  424. delete pFront;
  425. else
  426. {
  427. if (Pushback(pFront))
  428. {
  429. delete pFront;
  430. return FAILED;
  431. }
  432. }
  433. return SUCCESS;
  434. }
  435. //***************************************************************************
  436. //
  437. // CWQLScanner::SelectList
  438. //
  439. // Extracts all tokens up to the FROM keyword and builds a list
  440. // of selected properties/columns. FROM is left on the parse-stack on exit.
  441. //
  442. //***************************************************************************
  443. int CWQLScanner::SelectList()
  444. {
  445. // If the first token is FROM, then we have a SELECT FROM <rest>
  446. // which is the same as SELECT * FROM <rest>. We simply
  447. // alter the parse-stack and let the following loop handle it.
  448. // =============================================================
  449. WSLexToken *pTok = ExtractNext();
  450. if (!pTok)
  451. return SYNTAX_ERROR;
  452. if (pTok->m_nToken == WQL_TOK_FROM)
  453. {
  454. WSLexToken *pAsterisk = new WSLexToken;
  455. if (pAsterisk == NULL)
  456. return FAILED;
  457. pAsterisk->m_nToken = WQL_TOK_ASTERISK;
  458. pAsterisk->m_pszTokenText = Macro_CloneLPWSTR(L"*");
  459. if (!pAsterisk->m_pszTokenText)
  460. return FAILED;
  461. if (Pushback(pTok))
  462. {
  463. delete pTok;
  464. delete pAsterisk;
  465. return FAILED;
  466. }
  467. if (Pushback(pAsterisk))
  468. {
  469. delete pAsterisk;
  470. return FAILED;
  471. }
  472. }
  473. else
  474. {
  475. if (Pushback(pTok))
  476. {
  477. delete pTok;
  478. return FAILED;
  479. }
  480. }
  481. // Otherwise, some kind of column selection is present.
  482. // ====================================================
  483. BOOL bTerminate = FALSE;
  484. while (!bTerminate)
  485. {
  486. pTok = ExtractNext();
  487. if (pTok == 0)
  488. return SYNTAX_ERROR;
  489. // We must begin at a legal token.
  490. // ===============================
  491. if (pTok->m_nToken != WQL_TOK_EOF)
  492. {
  493. CTokenArray Tokens;
  494. if (Tokens.Add(pTok))
  495. {
  496. delete pTok;
  497. return FAILED;
  498. }
  499. while (1)
  500. {
  501. pTok = ExtractNext();
  502. if (pTok == 0 || pTok->m_nToken == WQL_TOK_EOF)
  503. {
  504. delete pTok;
  505. return SYNTAX_ERROR;
  506. }
  507. if (pTok->m_nToken == WQL_TOK_FROM)
  508. {
  509. if (Pushback(pTok))
  510. {
  511. delete pTok;
  512. return FAILED;
  513. }
  514. bTerminate = TRUE;
  515. break;
  516. }
  517. else if (pTok->m_nToken == WQL_TOK_COMMA)
  518. {
  519. delete pTok;
  520. break;
  521. }
  522. else
  523. {
  524. if (Tokens.Add(pTok))
  525. {
  526. delete pTok;
  527. return FAILED;
  528. }
  529. }
  530. }
  531. SWQLColRef *pColRef = new SWQLColRef;
  532. if (pColRef == 0)
  533. return FAILED;
  534. BOOL bRes = BuildSWQLColRef(Tokens, *pColRef);
  535. if (bRes)
  536. {
  537. if (m_aPropRefs.Add(pColRef))
  538. {
  539. delete pColRef;
  540. return FAILED;
  541. }
  542. }
  543. else
  544. {
  545. delete pColRef;
  546. return SYNTAX_ERROR;
  547. }
  548. }
  549. // Else an illegal token, such as WQL_TOK_EOF.
  550. // ===========================================
  551. else
  552. {
  553. delete pTok;
  554. return SYNTAX_ERROR;
  555. }
  556. }
  557. return SUCCESS;
  558. }
  559. //***************************************************************************
  560. //
  561. // CWQLScanner::ReduceSql89Joins
  562. //
  563. // Attempts to reduce the FROM clause, assuming it is based on SQL-89
  564. // join syntax or else a simple unary select.
  565. //
  566. // The supported forms are:
  567. //
  568. // FROM x
  569. // FROM x, y
  570. // FROM x as x1, y as y1
  571. // FROM x x1, y y1
  572. //
  573. // If incompatible tokens are encountered, the entire function
  574. // returns FALSE and the results are ignored, and the parse-stack
  575. // is unaffected, in essence, allowing backtracking to try the SQL-92
  576. // syntax branch instead.
  577. //
  578. //***************************************************************************
  579. BOOL CWQLScanner::ReduceSql89Joins()
  580. {
  581. int i = 0;
  582. // Parse the FROM keyword.
  583. // =======================
  584. WSLexToken *pCurr = (WSLexToken *) m_aTokens[i++];
  585. if (pCurr->m_nToken != WQL_TOK_FROM)
  586. return FALSE;
  587. pCurr = (WSLexToken *) m_aTokens[i++];
  588. while (1)
  589. {
  590. if (pCurr->m_nToken != WQL_TOK_IDENT)
  591. return FALSE;
  592. // If here, we are looking at the beginnings of a table ref.
  593. // =========================================================
  594. WSTableRef *pTRef = new WSTableRef;
  595. if (pTRef == 0)
  596. return FAILED;
  597. pTRef->m_pszTable = Macro_CloneLPWSTR(pCurr->m_pszTokenText);
  598. if (!pTRef->m_pszTable)
  599. return FAILED;
  600. pTRef->m_pszAlias = Macro_CloneLPWSTR(pCurr->m_pszTokenText);
  601. if (!pTRef->m_pszAlias)
  602. return FAILED;
  603. if (m_aTableRefs.Add(pTRef))
  604. {
  605. delete pTRef;
  606. return FAILED;
  607. }
  608. // Attempt to recognize an alias.
  609. // ==============================
  610. pCurr = (WSLexToken *) m_aTokens[i++];
  611. if (pCurr == WQL_TOK_EOF || pCurr->m_nToken == WQL_TOK_UNION)
  612. break;
  613. if (pCurr->m_nToken == WQL_TOK_AS)
  614. pCurr = (WSLexToken *) m_aTokens[i++];
  615. if (pCurr->m_nToken == WQL_TOK_COMMA)
  616. {
  617. pCurr = (WSLexToken *) m_aTokens[i++];
  618. continue;
  619. }
  620. if (pCurr->m_nToken == WQL_TOK_EOF || pCurr->m_nToken == WQL_TOK_UNION)
  621. break;
  622. if (pCurr->m_nToken != WQL_TOK_IDENT)
  623. return FALSE;
  624. delete [] pTRef->m_pszAlias;
  625. pTRef->m_pszAlias = Macro_CloneLPWSTR(pCurr->m_pszTokenText);
  626. if (!pTRef->m_pszAlias)
  627. return FALSE;
  628. // We have completely parsed a table reference.
  629. // Now we move on to the next one.
  630. // ============================================
  631. pCurr = (WSLexToken *) m_aTokens[i++];
  632. if (pCurr->m_nToken == WQL_TOK_EOF || pCurr->m_nToken == WQL_TOK_UNION)
  633. break;
  634. if (pCurr->m_nToken != WQL_TOK_COMMA)
  635. return FALSE;
  636. pCurr = (WSLexToken *) m_aTokens[i++];
  637. }
  638. if (m_aTableRefs.Size())
  639. return TRUE;
  640. return FALSE;
  641. }
  642. //***************************************************************************
  643. //
  644. // CWQLScanner::ReduceSql92Joins
  645. //
  646. // This scans SQL-92 JOIN syntax looking for table aliases. See the
  647. // algorithm at the end of this file.
  648. //
  649. //***************************************************************************
  650. BOOL CWQLScanner::ReduceSql92Joins()
  651. {
  652. WSLexToken *pCurrent = 0, *pRover = 0, *pRight = 0, *pLeft;
  653. int nNumTokens = m_aTokens.Size();
  654. DWORD dwNumJoins = 0;
  655. int iCurrBase = 0;
  656. for (int i = 0; i < nNumTokens; i++)
  657. {
  658. pCurrent = (WSLexToken *) m_aTokens[i];
  659. // If a JOIN token is found, we have a candidate.
  660. // ==============================================
  661. if (pCurrent->m_nToken == WQL_TOK_JOIN)
  662. {
  663. dwNumJoins++;
  664. // Analyze right-context.
  665. // ======================
  666. if (i + 1 < nNumTokens)
  667. pRover = PWSLexToken(m_aTokens[i + 1]);
  668. else
  669. pRover = NULL;
  670. if (pRover && pRover->m_nToken == WQL_TOK_IDENT)
  671. {
  672. // Check for aliased table by checking for
  673. // AS or two juxtaposed idents.
  674. // =======================================
  675. if (i + 2 < nNumTokens)
  676. pRight = PWSLexToken(m_aTokens[i + 2]);
  677. else
  678. pRight = NULL;
  679. if (pRight && pRight->m_nToken == WQL_TOK_AS)
  680. {
  681. if (i + 3 < nNumTokens)
  682. pRight = PWSLexToken(m_aTokens[i + 3]);
  683. else
  684. pRight = NULL;
  685. }
  686. if (pRight && pRight->m_nToken == WQL_TOK_IDENT)
  687. {
  688. WSTableRef *pTRef = new WSTableRef;
  689. if (pTRef == 0)
  690. return FAILED;
  691. pTRef->m_pszAlias = Macro_CloneLPWSTR(pRight->m_pszTokenText);
  692. if (!pTRef->m_pszAlias)
  693. return FAILED;
  694. pTRef->m_pszTable = Macro_CloneLPWSTR(pRover->m_pszTokenText);
  695. if (!pTRef->m_pszTable)
  696. return FAILED;
  697. if (m_aTableRefs.Add(pTRef))
  698. {
  699. delete pTRef;
  700. return FAILED;
  701. }
  702. }
  703. else // An alias wasn't used, just a simple table ref.
  704. {
  705. WSTableRef *pTRef = new WSTableRef;
  706. if (pTRef == 0)
  707. return FAILED;
  708. pTRef->m_pszAlias = Macro_CloneLPWSTR(pRover->m_pszTokenText);
  709. if (!pTRef->m_pszAlias)
  710. return FAILED;
  711. pTRef->m_pszTable = Macro_CloneLPWSTR(pRover->m_pszTokenText);
  712. if (!pTRef->m_pszTable)
  713. return FAILED;
  714. if (m_aTableRefs.Add(pTRef))
  715. {
  716. delete pTRef;
  717. return FAILED;
  718. }
  719. }
  720. // discontinue analysis of right-context.
  721. }
  722. // Analyze left-context.
  723. // =====================
  724. int nLeft = i - 1;
  725. if (nLeft >= 0)
  726. pRover = PWSLexToken(m_aTokens[nLeft--]);
  727. else
  728. continue; // No point in continuing
  729. // Verify the ANSI join syntax.
  730. if (nLeft)
  731. {
  732. int iTemp = nLeft;
  733. WSLexToken *pTemp = pRover;
  734. bool bInner = false;
  735. bool bDir = false;
  736. bool bOuter = false;
  737. bool bFail = false;
  738. bool bIdent = false;
  739. while (iTemp >= iCurrBase)
  740. {
  741. if (pTemp->m_nToken == WQL_TOK_INNER)
  742. {
  743. if (bOuter || bIdent || bInner)
  744. bFail = TRUE;
  745. bInner = true;
  746. }
  747. else if (pTemp->m_nToken == WQL_TOK_OUTER)
  748. {
  749. if (bInner || bIdent || bOuter)
  750. bFail = TRUE;
  751. bOuter = true;
  752. }
  753. else if (pTemp->m_nToken == WQL_TOK_FULL ||
  754. pTemp->m_nToken == WQL_TOK_LEFT ||
  755. pTemp->m_nToken == WQL_TOK_RIGHT
  756. )
  757. {
  758. if (bDir || bIdent)
  759. bFail = TRUE;
  760. bDir = true;
  761. }
  762. else
  763. bIdent = TRUE;
  764. // We are trying to enforce correct ANSI-92 joins
  765. // even though we don't support them ourselves:
  766. // OK: LEFT OUTER JOIN
  767. // OUTER LEFT JOIN
  768. // LEFT JOIN
  769. // INNER JOIN
  770. // NOT: LEFT LEFT JOIN
  771. // LEFT INNER JOIN
  772. // LEFT RIGHT JOIN
  773. // OUTER INNER JOIN
  774. // OUTER LEFT OUTER JOIN
  775. // OUTER GARBAGE LEFT JOIN
  776. // (no right side)
  777. if ((bDir && bInner) || bFail)
  778. return FALSE;
  779. pTemp = PWSLexToken(m_aTokens[iTemp--]);
  780. }
  781. }
  782. // Skip past potential JOIN modifiers : INNER, OUTER,
  783. // FULL, LEFT, RIGHT
  784. // ==================================================
  785. if (pRover->m_nToken == WQL_TOK_INNER ||
  786. pRover->m_nToken == WQL_TOK_OUTER ||
  787. pRover->m_nToken == WQL_TOK_FULL ||
  788. pRover->m_nToken == WQL_TOK_LEFT ||
  789. pRover->m_nToken == WQL_TOK_RIGHT
  790. )
  791. {
  792. if (nLeft >= 0)
  793. pRover = PWSLexToken(m_aTokens[nLeft--]);
  794. else
  795. pRover = 0;
  796. }
  797. if (pRover->m_nToken == WQL_TOK_INNER ||
  798. pRover->m_nToken == WQL_TOK_OUTER ||
  799. pRover->m_nToken == WQL_TOK_FULL ||
  800. pRover->m_nToken == WQL_TOK_LEFT ||
  801. pRover->m_nToken == WQL_TOK_RIGHT
  802. )
  803. {
  804. if (nLeft >= 0)
  805. pRover = PWSLexToken(m_aTokens[nLeft--]);
  806. else
  807. pRover = 0;
  808. }
  809. // Now we look to see if the roving pointer is pointing
  810. // to an ident.
  811. // ====================================================
  812. if (pRover && pRover->m_nToken != WQL_TOK_IDENT)
  813. {
  814. // No chance that we are looking at an aliased
  815. // table in a JOIN clause.
  816. // ===========================================
  817. continue;
  818. }
  819. iCurrBase = i;
  820. // If here, we are now possibliy looking at the second half
  821. // of an alias, the 'alias' name proper. We mark this
  822. // by leaving pRover alone and continue to move into the
  823. // left context with a different pointer.
  824. // ========================================================
  825. if (nLeft >= 0)
  826. pLeft = PWSLexToken(m_aTokens[nLeft--]);
  827. else
  828. pLeft = 0;
  829. if (pLeft && pLeft->m_nToken == WQL_TOK_AS)
  830. {
  831. if (nLeft >= 0)
  832. pLeft = PWSLexToken(m_aTokens[nLeft--]);
  833. else
  834. pLeft = 0;
  835. }
  836. // The critical test. Are we at an ident?
  837. // =======================================
  838. if (pLeft && pLeft->m_nToken == WQL_TOK_IDENT)
  839. {
  840. WSTableRef *pTRef = new WSTableRef;
  841. if (pTRef == 0)
  842. return FAILED;
  843. pTRef->m_pszAlias = Macro_CloneLPWSTR(pRover->m_pszTokenText);
  844. if (!pTRef->m_pszAlias)
  845. return FAILED;
  846. pTRef->m_pszTable = Macro_CloneLPWSTR(pLeft->m_pszTokenText);
  847. if (!pTRef->m_pszTable)
  848. return FAILED;
  849. if (m_aTableRefs.Add(pTRef))
  850. {
  851. delete pTRef;
  852. return FAILED;
  853. }
  854. }
  855. else if (pLeft && pLeft->m_nToken == WQL_TOK_FROM)
  856. {
  857. WSTableRef *pTRef = new WSTableRef;
  858. if (pTRef == 0)
  859. return FAILED;
  860. pTRef->m_pszAlias = Macro_CloneLPWSTR(pRover->m_pszTokenText);
  861. if (!pTRef->m_pszAlias)
  862. return FAILED;
  863. pTRef->m_pszTable = Macro_CloneLPWSTR(pRover->m_pszTokenText);
  864. if (!pTRef->m_pszTable)
  865. return FAILED;
  866. if (m_aTableRefs.Add(pTRef))
  867. {
  868. delete pTRef;
  869. return FAILED;
  870. }
  871. if (nLeft >= 0)
  872. {
  873. pLeft = PWSLexToken(m_aTokens[nLeft--]);
  874. if (pLeft && pLeft->m_nToken == WQL_TOK_FROM)
  875. return FALSE;
  876. }
  877. }
  878. }
  879. // Find next JOIN occurrence
  880. }
  881. // Make sure there are two sides to every join reference.
  882. if (dwNumJoins+1 != (DWORD)m_aTableRefs.Size())
  883. return FALSE;
  884. return TRUE;
  885. }
  886. //***************************************************************************
  887. //
  888. //***************************************************************************
  889. void CWQLScanner::Dump()
  890. {
  891. WSLexToken *pCurrent = 0;
  892. printf("---Token Stream----\n");
  893. for (int i = 0; i < m_aTokens.Size(); i++)
  894. {
  895. pCurrent = (WSLexToken *) m_aTokens[i];
  896. printf("Token %d <%S>\n", pCurrent->m_nToken, pCurrent->m_pszTokenText);
  897. }
  898. printf("---Table Refs---\n");
  899. for (i = 0; i < m_aTableRefs.Size(); i++)
  900. {
  901. WSTableRef *pTRef = (WSTableRef *) m_aTableRefs[i];
  902. printf("Table = %S Alias = %S\n", pTRef->m_pszTable, pTRef->m_pszAlias);
  903. }
  904. if (!m_bCount)
  905. {
  906. printf("---Select List---\n");
  907. for (i = 0; i < m_aPropRefs.Size(); i++)
  908. {
  909. SWQLColRef *pCRef = (SWQLColRef *) m_aPropRefs[i];
  910. pCRef->DebugDump();
  911. }
  912. }
  913. else
  914. printf(" -> COUNT query\n");
  915. printf("\n\n---<end of dump>---\n\n");
  916. }
  917. /*---------------------------------------------------------------------------
  918. Algorithm for detecting aliased tables in SQL-92 join syntax.
  919. The JOIN keyword must appear.
  920. It may appear in several contexts which are not
  921. relevant to the aliasing problem, such as the following:
  922. select distinct t1a.name, t2a.id, t3.value from
  923. (t1 as t1a join t2 as t2a on t1a.name = t2a.name)
  924. join
  925. (t1 as t1b join t3 on t1b.id = t3.id and (t3.id = t1b.id or t1b.id = t3.id))
  926. on
  927. t1a.id = t3.id
  928. where a = b and c = d
  929. where the middle join is against anonymous result sets.
  930. When analyzing the JOIN, we can easily parse the right-context. Either
  931. an identifier follows (possibly further followed by AS),and an optional
  932. identifier if the JOIN is aliased. Otherwise, we hit ON immediately, or
  933. a parenthesis.
  934. The problem is the left-context of the JOIN token.
  935. For an alias to occur, an identifier must appear immediately to
  936. the left of the JOIN.
  937. id JOIN id2 as id3 ON ...
  938. ^
  939. If here, there is a chance we are looking at the left hand side of a
  940. SQL92 join, a table reference. However, we might be looking at the end of
  941. an ON clause which ends in an identifier:
  942. idx = id JOIN id2 as id3 ON...
  943. ^
  944. To disambiguate, we have to do further analysis of left context.
  945. Consider the follow left-context possibilities:
  946. (1) t1 AS id JOIN id2 as id3 ON
  947. ^
  948. (2) t1 id JOIN id2 as id3 ON
  949. ^
  950. (3) <keyword (except AS)> id JOIN id2 as id3 ON
  951. ^
  952. (4) on x <rel op> id JOIN id2 as id3 ON
  953. ^
  954. Once we have identified <id>, we have to consider the above cases.
  955. (1) Case 1 is easy. An AS clearly tells us we have an alias
  956. and we know how to get at the table and alias names.
  957. (2) Case 2 is easy. Two juxtaposed identifiers to the left always
  958. indicates an alias.
  959. In all other cases, like (3) and (4), etc., the table is not
  960. aliased anyway. Therefore, we only have to determine whether we
  961. are looking at an unaliased table name or the trailing end of
  962. another construct like an ON clause. This is easy. Only the
  963. FROM keyword can precede <id> if <id> is a simple table name.
  964. ---------------------------------------------------------------------------
  965. */
  966. //***************************************************************************
  967. //
  968. // CWQLScanner::BuildSWQLColRef
  969. //
  970. //***************************************************************************
  971. BOOL CWQLScanner::BuildSWQLColRef(
  972. IN CFlexArray &aTokens,
  973. IN OUT SWQLColRef &ColRef // Empty on entry
  974. )
  975. {
  976. if (aTokens.Size() == 0)
  977. return FALSE;
  978. int nCurrent = 0;
  979. WSLexToken *pTok = PWSLexToken(aTokens[nCurrent++]);
  980. // Initial state: single asterisk or else prop name.
  981. // =================================================
  982. if (pTok->m_nToken == WQL_TOK_ASTERISK && aTokens.Size() == 1)
  983. {
  984. ColRef.m_pColName = Macro_CloneLPWSTR(L"*");
  985. if (!ColRef.m_pColName)
  986. return FALSE;
  987. ColRef.m_dwFlags = WQL_FLAG_ASTERISK;
  988. ColRef.m_pQName = new SWQLQualifiedName;
  989. if (ColRef.m_pQName == 0)
  990. return FALSE;
  991. SWQLQualifiedNameField *pField = new SWQLQualifiedNameField;
  992. if (pField == 0)
  993. return FALSE;
  994. pField->m_pName = Macro_CloneLPWSTR(L"*");
  995. if (!pField->m_pName)
  996. return FALSE;
  997. if (ColRef.m_pQName->Add(pField))
  998. {
  999. delete pField;
  1000. return FALSE;
  1001. }
  1002. return TRUE;
  1003. }
  1004. // If not an identifier, we have an error.
  1005. // =======================================
  1006. else if (pTok->m_nToken == WQL_TOK_EOF)
  1007. return FALSE;
  1008. // If here, we have an identifier.
  1009. // ===============================
  1010. ColRef.m_pQName = new SWQLQualifiedName;
  1011. if (ColRef.m_pQName == NULL)
  1012. return FALSE;
  1013. SWQLQualifiedNameField *pField = new SWQLQualifiedNameField;
  1014. if (pField == 0)
  1015. return FALSE;
  1016. pField->m_pName = Macro_CloneLPWSTR(pTok->m_pszTokenText);
  1017. if (!pField->m_pName)
  1018. return FALSE;
  1019. if (ColRef.m_pQName->Add(pField))
  1020. {
  1021. delete pField;
  1022. return FALSE;
  1023. }
  1024. // Subsequent states.
  1025. // ==================
  1026. while (1)
  1027. {
  1028. if (nCurrent == aTokens.Size())
  1029. break;
  1030. pTok = PWSLexToken(aTokens[nCurrent++]);
  1031. if (pTok->m_nToken == WQL_TOK_DOT)
  1032. {
  1033. pField = new SWQLQualifiedNameField;
  1034. if (pField == 0)
  1035. return FALSE;
  1036. if (ColRef.m_pQName->Add(pField))
  1037. {
  1038. delete pField;
  1039. return FALSE;
  1040. }
  1041. if (nCurrent == aTokens.Size())
  1042. return FALSE;
  1043. pTok = PWSLexToken(aTokens[nCurrent++]);
  1044. if (pTok->m_nToken != WQL_TOK_IDENT &&
  1045. pTok->m_nToken != WQL_TOK_ASTERISK
  1046. )
  1047. return FALSE;
  1048. pField->m_pName = Macro_CloneLPWSTR(pTok->m_pszTokenText);
  1049. if (!pField->m_pName)
  1050. return FALSE;
  1051. }
  1052. else if (pTok->m_nToken == WQL_TOK_OPEN_BRACKET)
  1053. {
  1054. return FALSE; // Not supported at present!
  1055. }
  1056. else // illegal token
  1057. return FALSE;
  1058. }
  1059. // Post-process. If the name is not complex, then we
  1060. // can fill out fields of ColRef.
  1061. // ==================================================
  1062. if (ColRef.m_pQName->GetNumNames() == 2)
  1063. {
  1064. ColRef.m_pTableRef = Macro_CloneLPWSTR(ColRef.m_pQName->GetName(0));
  1065. if (!ColRef.m_pTableRef)
  1066. return FALSE;
  1067. ColRef.m_pColName = Macro_CloneLPWSTR(ColRef.m_pQName->GetName(1));
  1068. if (!ColRef.m_pColName)
  1069. return FALSE;
  1070. if (wbem_wcsicmp(ColRef.m_pColName, L"NULL") == 0)
  1071. ColRef.m_dwFlags |= WQL_FLAG_NULL;
  1072. }
  1073. else if (ColRef.m_pQName->GetNumNames() == 1)
  1074. {
  1075. LPWSTR pName = ColRef.m_pQName->GetName(0);
  1076. ColRef.m_pColName = Macro_CloneLPWSTR(pName);
  1077. if (!ColRef.m_pColName)
  1078. return FALSE;
  1079. if (0 == wbem_wcsicmp(ColRef.m_pColName, L"NULL"))
  1080. ColRef.m_dwFlags |= WQL_FLAG_NULL;
  1081. }
  1082. else
  1083. {
  1084. ColRef.m_pTableRef = Macro_CloneLPWSTR(ColRef.m_pQName->GetName(0));
  1085. if (!ColRef.m_pTableRef)
  1086. return FALSE;
  1087. ColRef.m_dwFlags = WQL_FLAG_COMPLEX_NAME;
  1088. }
  1089. return TRUE;
  1090. }
  1091. const LPWSTR CWQLScanner::AliasToTable(LPWSTR pszAlias)
  1092. {
  1093. if (pszAlias == 0)
  1094. return 0;
  1095. for (int i = 0; i < m_aTableRefs.Size(); i++)
  1096. {
  1097. WSTableRef *pTRef = (WSTableRef *) m_aTableRefs[i];
  1098. if (wbem_wcsicmp(pszAlias, pTRef->m_pszAlias) == 0)
  1099. return pTRef->m_pszTable;
  1100. if (wbem_wcsicmp(pszAlias, pTRef->m_pszTable) == 0)
  1101. return pTRef->m_pszTable;
  1102. }
  1103. return 0;
  1104. }
  1105.