PageRenderTime 27ms CodeModel.GetById 11ms RepoModel.GetById 0ms app.codeStats 1ms

/SmarterSql/SmarterSql/Tree/InStatement.cs

#
C# | 739 lines | 523 code | 59 blank | 157 comment | 293 complexity | c4a0e5fc28ebdf8fb4b21f9c6738b208 MD5 | raw file
  1. // ---------------------------------
  2. // SmarterSql (c) Johan Sassner 2008
  3. // ---------------------------------
  4. using System;
  5. using System.Collections.Generic;
  6. using Sassner.SmarterSql.Objects;
  7. using Sassner.SmarterSql.Parsing;
  8. using Sassner.SmarterSql.ParsingObjects;
  9. using Sassner.SmarterSql.ParsingUtils;
  10. using Sassner.SmarterSql.Utils;
  11. namespace Sassner.SmarterSql.Tree {
  12. public class InStatement {
  13. #region GetIfAnyNextValidToken
  14. /// <summary>
  15. /// Return true if the next token is of any kind from the suppled TokenInfo objects, else false
  16. /// </summary>
  17. /// <param name="lstTokens"></param>
  18. /// <param name="i"></param>
  19. /// <param name="nextToken"></param>
  20. /// <param name="kindToFind"></param>
  21. /// <returns></returns>
  22. public static bool GetIfAnyNextValidToken(List<TokenInfo> lstTokens, ref int i, out TokenInfo nextToken, params TokenKind[] kindToFind) {
  23. nextToken = null;
  24. int start = i;
  25. i++;
  26. for (int j = 0; j < kindToFind.Length; j++) {
  27. nextToken = GetNextNonCommentToken(lstTokens, ref i);
  28. if (null != nextToken && nextToken.Kind == kindToFind[j]) {
  29. return true;
  30. }
  31. }
  32. i = start;
  33. return false;
  34. }
  35. public static bool GetIfAnyNextValidToken(List<TokenInfo> lstTokens, ref int i, out TokenInfo nextToken, params TokenType[] typeToFind) {
  36. nextToken = null;
  37. int start = i;
  38. i++;
  39. for (int j = 0; j < typeToFind.Length; j++) {
  40. nextToken = GetNextNonCommentToken(lstTokens, ref i);
  41. if (null != nextToken && nextToken.Type == typeToFind[j]) {
  42. return true;
  43. }
  44. }
  45. i = start;
  46. return false;
  47. }
  48. #endregion
  49. #region GetIfAllNextValidToken
  50. /// <summary>
  51. /// Return true if all of the supplied TokenKinds are found after each others, else false
  52. /// </summary>
  53. /// <param name="lstTokens"></param>
  54. /// <param name="i"></param>
  55. /// <param name="nextToken"></param>
  56. /// <param name="kindToFind"></param>
  57. /// <returns></returns>
  58. public static bool GetIfAllNextValidToken(List<TokenInfo> lstTokens, ref int i, out TokenInfo nextToken, params TokenKind[] kindToFind) {
  59. nextToken = null;
  60. int start = i;
  61. for (int j = 0; j < kindToFind.Length; j++) {
  62. i++;
  63. nextToken = GetNextNonCommentToken(lstTokens, ref i);
  64. if (null == nextToken || nextToken.Kind != kindToFind[j]) {
  65. i = start;
  66. return false;
  67. }
  68. }
  69. return true;
  70. }
  71. public static bool GetIfAllNextValidToken(List<TokenInfo> lstTokens, ref int i, out TokenInfo nextToken, params TokenType[] typeToFind) {
  72. nextToken = null;
  73. int start = i;
  74. for (int j = 0; j < typeToFind.Length; j++) {
  75. i++;
  76. nextToken = GetNextNonCommentToken(lstTokens, ref i);
  77. if (null == nextToken || nextToken.Type != typeToFind[j]) {
  78. i = start;
  79. return false;
  80. }
  81. }
  82. return true;
  83. }
  84. #endregion
  85. #region GetNextNonCommentToken methods
  86. /// <summary>
  87. /// Get first next token not being a comment token
  88. /// </summary>
  89. /// <param name="lstTokens"></param>
  90. /// <param name="currentIndex"></param>
  91. /// <returns></returns>
  92. public static TokenInfo GetNextNonCommentToken(List<TokenInfo> lstTokens, ref int currentIndex) {
  93. TokenInfo token = null;
  94. if (currentIndex >= 0) {
  95. while (currentIndex <= lstTokens.Count - 1) {
  96. token = lstTokens[currentIndex];
  97. if (token.Type == TokenType.Comment) {
  98. if (currentIndex <= lstTokens.Count - 1) {
  99. currentIndex++;
  100. } else {
  101. return null;
  102. }
  103. } else {
  104. return token;
  105. }
  106. }
  107. }
  108. return token;
  109. }
  110. /// <summary>
  111. /// Get first next token not being a comment token
  112. /// </summary>
  113. /// <param name="lstTokens"></param>
  114. /// <param name="currentIndex"></param>
  115. /// <returns></returns>
  116. public static TokenInfo GetNextNonCommentToken(List<TokenInfo> lstTokens, int currentIndex) {
  117. TokenInfo token = null;
  118. if (currentIndex >= 0) {
  119. while (currentIndex <= lstTokens.Count - 1) {
  120. token = lstTokens[currentIndex];
  121. if (token.Type == TokenType.Comment) {
  122. if (currentIndex <= lstTokens.Count - 1) {
  123. currentIndex++;
  124. } else {
  125. return null;
  126. }
  127. } else {
  128. return token;
  129. }
  130. }
  131. }
  132. return token;
  133. }
  134. /// <summary>
  135. /// Get first next token not being a comment token
  136. /// </summary>
  137. /// <param name="lstTokens"></param>
  138. /// <param name="currentIndex"></param>
  139. /// <param name="defaultIndex"></param>
  140. /// <returns></returns>
  141. public static int GetNextNonCommentToken(List<TokenInfo> lstTokens, int currentIndex, int defaultIndex) {
  142. TokenInfo token;
  143. if (currentIndex >= 0) {
  144. while (currentIndex <= lstTokens.Count - 1) {
  145. token = lstTokens[currentIndex];
  146. if (token.Type == TokenType.Comment) {
  147. if (currentIndex <= lstTokens.Count - 1) {
  148. currentIndex++;
  149. } else {
  150. return defaultIndex;
  151. }
  152. } else {
  153. return currentIndex;
  154. }
  155. }
  156. }
  157. return currentIndex;
  158. }
  159. #endregion
  160. #region GetPreviousNonCommentToken methods
  161. /// <summary>
  162. /// Get first previous token that ain't a comment token
  163. /// </summary>
  164. /// <param name="lstTokens"></param>
  165. /// <param name="currentIndex"></param>
  166. /// <returns></returns>
  167. public static TokenInfo GetPreviousNonCommentToken(List<TokenInfo> lstTokens, ref int currentIndex) {
  168. TokenInfo token = null;
  169. if (currentIndex <= lstTokens.Count - 1) {
  170. while (currentIndex >= 0) {
  171. token = lstTokens[currentIndex];
  172. if (token.Type == TokenType.Comment) {
  173. if (currentIndex - 1 >= 0) {
  174. currentIndex--;
  175. } else {
  176. return null;
  177. }
  178. } else {
  179. return token;
  180. }
  181. }
  182. }
  183. return token;
  184. }
  185. /// <summary>
  186. /// Get first previous token that ain't a comment token
  187. /// </summary>
  188. /// <param name="lstTokens"></param>
  189. /// <param name="currentIndex"></param>
  190. /// <returns></returns>
  191. public static TokenInfo GetPreviousNonCommentToken(List<TokenInfo> lstTokens, int currentIndex) {
  192. TokenInfo token = null;
  193. if (currentIndex <= lstTokens.Count - 1) {
  194. while (currentIndex >= 0) {
  195. token = lstTokens[currentIndex];
  196. if (token.Type == TokenType.Comment) {
  197. if (currentIndex - 1 >= 0) {
  198. currentIndex--;
  199. } else {
  200. return null;
  201. }
  202. } else {
  203. return token;
  204. }
  205. }
  206. }
  207. return token;
  208. }
  209. /// <summary>
  210. /// Get first previous token that ain't a comment token
  211. /// </summary>
  212. /// <param name="lstTokens"></param>
  213. /// <param name="currentIndex"></param>
  214. /// <param name="defaultIndex"></param>
  215. /// <returns></returns>
  216. public static int GetPreviousNonCommentToken(List<TokenInfo> lstTokens, int currentIndex, int defaultIndex) {
  217. TokenInfo token;
  218. if (currentIndex <= lstTokens.Count - 1) {
  219. while (currentIndex >= 0) {
  220. token = lstTokens[currentIndex];
  221. if (token.Type == TokenType.Comment) {
  222. if (currentIndex - 1 >= 0) {
  223. currentIndex--;
  224. } else {
  225. return defaultIndex;
  226. }
  227. } else {
  228. return currentIndex;
  229. }
  230. }
  231. }
  232. return currentIndex;
  233. }
  234. #endregion
  235. public static bool IsDatabase(int currentIndex, List<TokenInfo> tokens, Server activeServer, out Connection foundConnection) {
  236. foundConnection = null;
  237. if (currentIndex >= 0) {
  238. string database = tokens[currentIndex].Token.UnqoutedImage;
  239. foreach (Connection connection in activeServer.Connections) {
  240. if (connection.ActiveConnection.DatabaseName.Equals(database, StringComparison.OrdinalIgnoreCase)) {
  241. foundConnection = connection;
  242. return true;
  243. }
  244. }
  245. if (Server.AddNewConnection(activeServer, database, out foundConnection)) {
  246. return true;
  247. }
  248. }
  249. return false;
  250. }
  251. /// <summary>
  252. /// Find out if the token at the current index is a schema
  253. /// </summary>
  254. /// <param name="currentIndex"></param>
  255. /// <param name="activeServer"></param>
  256. /// <param name="foundSchema"></param>
  257. /// <param name="foundConnection"></param>
  258. /// <param name="tokens"></param>
  259. /// <param name="activeConnection"></param>
  260. /// <returns></returns>
  261. public static bool IsSchema(int currentIndex, List<TokenInfo> tokens, Connection activeConnection, Server activeServer, out SysObjectSchema foundSchema, out Connection foundConnection) {
  262. if (currentIndex >= 0) {
  263. string database = activeConnection.ActiveConnection.DatabaseName;
  264. string schema = tokens[currentIndex].Token.UnqoutedImage;
  265. if (schema.Equals(".")) {
  266. schema = activeServer.GetDefaultSchema();
  267. if (currentIndex - 1 >= 0 && tokens[currentIndex - 1].Kind == TokenKind.Name) {
  268. database = tokens[currentIndex - 1].Token.UnqoutedImage;
  269. }
  270. } else {
  271. if (currentIndex - 2 >= 0 && tokens[currentIndex - 1].Kind == TokenKind.Dot && tokens[currentIndex - 2].Kind == TokenKind.Name) {
  272. database = tokens[currentIndex - 2].Token.UnqoutedImage;
  273. }
  274. }
  275. return IsSchema(activeServer, activeConnection, schema, database, out foundSchema, out foundConnection);
  276. }
  277. foundSchema = null;
  278. foundConnection = null;
  279. return false;
  280. }
  281. /// <summary>
  282. /// See if we are a candidate for auto-generating column_list
  283. /// INSERT
  284. /// [ TOP ( expression ) [ PERCENT ] ]
  285. /// [ INTO ]
  286. /// { <object> | rowset_function_limited [ WITH ( <Table_Hint_Limited> [ ...n ] ) ] }
  287. /// {
  288. /// [ ( column_list ) ]
  289. /// </summary>
  290. /// <param name="lstTokens"></param>
  291. /// <param name="currentIndex"></param>
  292. ///<param name="foundTableSource"> </param>
  293. ///<returns></returns>
  294. public static bool IsCandidateForInsertColumns(List<TokenInfo> lstTokens, int currentIndex, out TableSource foundTableSource) {
  295. foundTableSource = null;
  296. TokenInfo token = GetPreviousNonCommentToken(lstTokens, ref currentIndex);
  297. if (null != token && token.Kind == TokenKind.LeftParenthesis) {
  298. currentIndex--;
  299. token = GetPreviousNonCommentToken(lstTokens, ref currentIndex);
  300. if (null == token) {
  301. return false;
  302. }
  303. // Handle: [ WITH ( <Table_Hint_Limited> [ ...n ] ) ]
  304. if (token.Kind == TokenKind.RightParenthesis && -1 != token.MatchingParenToken && currentIndex > token.MatchingParenToken) {
  305. currentIndex = token.MatchingParenToken - 1;
  306. token = GetPreviousNonCommentToken(lstTokens, ref currentIndex);
  307. if (null == token || token.Kind != TokenKind.KeywordWith) {
  308. return false;
  309. }
  310. currentIndex--;
  311. token = GetPreviousNonCommentToken(lstTokens, ref currentIndex);
  312. if (null == token) {
  313. return false;
  314. }
  315. }
  316. // Handle: <object>
  317. token = null;
  318. foreach (TableSource tableSource in TextEditor.CurrentWindowData.Parser.TableSources) {
  319. if (tableSource.Table.EndIndex == currentIndex) {
  320. foundTableSource = tableSource;
  321. currentIndex = tableSource.Table.StartIndex - 1;
  322. token = GetPreviousNonCommentToken(lstTokens, ref currentIndex);
  323. break;
  324. }
  325. }
  326. if (null != token) {
  327. // Handle: [ INTO ]
  328. if (token.Kind == TokenKind.KeywordInto) {
  329. currentIndex--;
  330. token = GetPreviousNonCommentToken(lstTokens, ref currentIndex);
  331. if (null == token) {
  332. return false;
  333. }
  334. }
  335. // Handle: [ TOP ( expression ) [ PERCENT ] ]
  336. if (token.Kind == TokenKind.KeywordPercent) {
  337. currentIndex--;
  338. token = GetPreviousNonCommentToken(lstTokens, ref currentIndex);
  339. if (null == token) {
  340. return false;
  341. }
  342. }
  343. if (token.Kind == TokenKind.RightParenthesis && -1 != token.MatchingParenToken && currentIndex > token.MatchingParenToken) {
  344. currentIndex = token.MatchingParenToken - 1;
  345. token = GetPreviousNonCommentToken(lstTokens, ref currentIndex);
  346. if (null == token || token.Kind != TokenKind.KeywordTop) {
  347. return false;
  348. }
  349. currentIndex--;
  350. token = GetPreviousNonCommentToken(lstTokens, ref currentIndex);
  351. if (null == token) {
  352. return false;
  353. }
  354. }
  355. // Handle: INSERT
  356. if (token.Kind == TokenKind.KeywordInsert) {
  357. return true;
  358. }
  359. }
  360. }
  361. return false;
  362. }
  363. /// <summary>
  364. /// Is the supplied schemaName a schema in database databaseName?
  365. /// </summary>
  366. /// <param name="objActiveServer"></param>
  367. /// <param name="objActiveConnection"></param>
  368. /// <param name="schemaName"></param>
  369. /// <param name="databaseName"></param>
  370. /// <param name="foundSchema"></param>
  371. /// <param name="foundConnection"></param>
  372. /// <returns></returns>
  373. public static bool IsSchema(Server objActiveServer, Connection objActiveConnection, string schemaName, string databaseName, out SysObjectSchema foundSchema, out Connection foundConnection) {
  374. foundSchema = null;
  375. foundConnection = null;
  376. if (null != objActiveConnection) {
  377. if (objActiveConnection.ActiveConnection.DatabaseName.Equals(databaseName, StringComparison.OrdinalIgnoreCase)) {
  378. if (IsSchema(objActiveConnection, schemaName, databaseName, out foundSchema)) {
  379. foundConnection = objActiveConnection;
  380. return true;
  381. }
  382. } else {
  383. foreach (Connection connection in objActiveServer.Connections) {
  384. if (connection.ActiveConnection.DatabaseName.Equals(databaseName, StringComparison.OrdinalIgnoreCase)) {
  385. foundConnection = connection;
  386. return IsSchema(connection, schemaName, databaseName, out foundSchema);
  387. }
  388. }
  389. if (Server.AddNewConnection(objActiveServer, databaseName, out objActiveConnection)) {
  390. foundConnection = objActiveConnection;
  391. return IsSchema(objActiveConnection, schemaName, databaseName, out foundSchema);
  392. }
  393. return false;
  394. }
  395. }
  396. return false;
  397. }
  398. /// <summary>
  399. /// Is the supplied schemaName a schema in database databaseName?
  400. /// </summary>
  401. /// <param name="objActiveConnection"></param>
  402. /// <param name="schemaName"></param>
  403. /// <param name="databaseName"></param>
  404. /// <param name="foundSchema"></param>
  405. /// <returns></returns>
  406. private static bool IsSchema(Connection objActiveConnection, string schemaName, string databaseName, out SysObjectSchema foundSchema) {
  407. foundSchema = null;
  408. if (null != objActiveConnection) {
  409. List<SysObjectSchema> sysObjectSchemas = objActiveConnection.GetSysObjectSchemas();
  410. foreach (SysObjectSchema schema in sysObjectSchemas) {
  411. if (null != schema.Schema && schemaName.Equals(schema.Schema, StringComparison.OrdinalIgnoreCase) && schema.Connection.ActiveConnection.DatabaseName.Equals(databaseName, StringComparison.OrdinalIgnoreCase)) {
  412. foundSchema = schema;
  413. return true;
  414. }
  415. }
  416. }
  417. return false;
  418. }
  419. //DECLARE
  420. // {{ @local_variable [AS] data_type }
  421. // | { @cursor_variable_name CURSOR }
  422. // | { @table_variable_name [AS] < table_type_definition > }
  423. // } [ ,...n]
  424. public static bool IsInStatementDeclare(List<TokenInfo> lstTokens, int currentIndex) {
  425. int parenLevel = -1;
  426. while (currentIndex >= 0) {
  427. TokenInfo token = lstTokens[currentIndex];
  428. if (-1 == parenLevel) {
  429. parenLevel = token.ParenLevel;
  430. }
  431. if (token.ParenLevel == parenLevel) {
  432. if (token.Kind == TokenKind.KeywordDeclare) {
  433. return true;
  434. }
  435. if (!(token.Type == TokenType.Comment || token.Type == TokenType.DataType || token.Kind == TokenKind.Comma || token.Kind == TokenKind.RightParenthesis || token.Type == TokenType.Identifier || token.Kind == TokenKind.KeywordAs || token.Kind == TokenKind.KeywordCursor || token.Kind == TokenKind.Variable)) {
  436. return false;
  437. }
  438. }
  439. currentIndex--;
  440. }
  441. return false;
  442. }
  443. /// <summary>
  444. /// Find out if this token is an table alias. Look for AS keyword and/or table name / derived table
  445. /// </summary>
  446. /// <param name="lstTokens"></param>
  447. /// <param name="currentIndex"></param>
  448. /// <param name="table"></param>
  449. /// <returns></returns>
  450. public static bool IsTableAlias(List<TokenInfo> lstTokens, int currentIndex, string table) {
  451. if (currentIndex - 1 >= 0) {
  452. currentIndex--;
  453. TokenInfo token = GetPreviousNonCommentToken(lstTokens, ref currentIndex);
  454. if (token.Kind == TokenKind.KeywordAs) {
  455. if (currentIndex - 1 >= 0) {
  456. currentIndex--;
  457. token = GetPreviousNonCommentToken(lstTokens, ref currentIndex);
  458. }
  459. }
  460. if (token.Type == TokenType.Identifier) {
  461. if (token.Token.UnqoutedImage.Equals(table, StringComparison.OrdinalIgnoreCase)) {
  462. return true;
  463. }
  464. } else if (token.Kind == TokenKind.RightParenthesis) {
  465. return true;
  466. }
  467. }
  468. return false;
  469. }
  470. // DELETE
  471. // [ TOP ( expression ) [ PERCENT ] ]
  472. // [ FROM ]
  473. // .....
  474. public static bool IsInStatementDelete(List<TokenInfo> lstTokens, int currentIndex) {
  475. int parenLevel = -1;
  476. while (currentIndex >= 0) {
  477. TokenInfo token = lstTokens[currentIndex];
  478. if (-1 == parenLevel) {
  479. parenLevel = token.ParenLevel;
  480. }
  481. if (token.ParenLevel == parenLevel) {
  482. if (token.Kind == TokenKind.KeywordDelete) {
  483. return true;
  484. }
  485. if (!(token.Type == TokenType.Comment || token.Kind == TokenKind.KeywordFrom || token.Kind == TokenKind.KeywordPercent || token.Kind == TokenKind.KeywordTop || token.Kind == TokenKind.RightParenthesis)) {
  486. return false;
  487. }
  488. }
  489. currentIndex--;
  490. }
  491. return false;
  492. }
  493. //[ { EXEC | EXECUTE } ]
  494. // {
  495. // [ @return_status = ]
  496. // { module_name [ ;number ] | @module_name_var }
  497. //
  498. // NOTE: Only part of Execute statement check is validated
  499. public static bool IsInStatementExecute(List<TokenInfo> lstTokens, int currentIndex) {
  500. while (currentIndex >= 0) {
  501. TokenInfo token = lstTokens[currentIndex];
  502. if (token.Kind == TokenKind.KeywordExec) {
  503. return true;
  504. }
  505. if (!(token.Type == TokenType.Comment || token.Kind == TokenKind.Assign || token.Kind == TokenKind.Variable)) {
  506. return false;
  507. }
  508. currentIndex--;
  509. }
  510. return false;
  511. }
  512. /// <summary>
  513. /// Is this a TableSample statement? If so return the end index of the last token
  514. /// TABLESAMPLE [SYSTEM] ( sample_number [ PERCENT | ROWS ] ) [ REPEATABLE ( repeat_seed ) ]
  515. /// </summary>
  516. /// <param name="tokens"></param>
  517. /// <param name="currentIndex"></param>
  518. /// <param name="statementEndIndex"></param>
  519. /// <returns></returns>
  520. public static bool IsInStatementTableSample(List<TokenInfo> tokens, int currentIndex, out int statementEndIndex) {
  521. statementEndIndex = -1;
  522. TokenInfo token = GetNextNonCommentToken(tokens, ref currentIndex);
  523. if (null != token && token.Kind == TokenKind.KeywordTableSample) {
  524. currentIndex++;
  525. token = GetNextNonCommentToken(tokens, ref currentIndex);
  526. if (null != token && token.Kind == TokenKind.KeywordSystem) {
  527. currentIndex++;
  528. token = GetNextNonCommentToken(tokens, ref currentIndex);
  529. }
  530. if (null != token) {
  531. int matchingParenToken = token.MatchingParenToken;
  532. // ... ( sample_number [ PERCENT | ROWS ] ) [ REPEATABLE ( repeat_seed ) ]
  533. if (token.Kind == TokenKind.LeftParenthesis && -1 != matchingParenToken && currentIndex < matchingParenToken) {
  534. currentIndex++;
  535. token = GetNextNonCommentToken(tokens, ref currentIndex);
  536. if (null != token && token.Kind == TokenKind.ValueNumber) {
  537. currentIndex++;
  538. token = GetNextNonCommentToken(tokens, ref currentIndex);
  539. if (null != token && (token.Kind == TokenKind.KeywordPercent || token.Kind == TokenKind.KeywordRows)) {
  540. currentIndex++;
  541. token = GetNextNonCommentToken(tokens, ref currentIndex);
  542. }
  543. if (null != token) {
  544. currentIndex = matchingParenToken + 1;
  545. token = GetNextNonCommentToken(tokens, ref currentIndex);
  546. if (null != token && token.Kind == TokenKind.KeywordRepeatable) {
  547. currentIndex++;
  548. token = GetNextNonCommentToken(tokens, ref currentIndex);
  549. if (null != token) {
  550. matchingParenToken = token.MatchingParenToken;
  551. if (token.Kind == TokenKind.LeftParenthesis && -1 != matchingParenToken && currentIndex < matchingParenToken) {
  552. currentIndex++;
  553. token = GetNextNonCommentToken(tokens, ref currentIndex);
  554. if (null != token && token.Kind == TokenKind.ValueNumber) {
  555. statementEndIndex = matchingParenToken;
  556. return true;
  557. }
  558. }
  559. }
  560. } else {
  561. statementEndIndex = currentIndex;
  562. return true;
  563. }
  564. }
  565. }
  566. }
  567. }
  568. }
  569. return false;
  570. }
  571. /// <summary>
  572. /// Is this a WITH(tablehint) statement? If so return the end index of the last token
  573. /// WITH ( < table_hint > [ [ , ]...n ] )
  574. /// </summary>
  575. /// <param name="tokens"></param>
  576. /// <param name="currentIndex"></param>
  577. /// <param name="staticData"></param>
  578. /// <param name="statementEndIndex"></param>
  579. /// <returns></returns>
  580. public static bool IsInStatementWithTableHint(List<TokenInfo> tokens, int currentIndex, StaticData staticData, out int statementEndIndex) {
  581. statementEndIndex = -1;
  582. TokenInfo token = GetNextNonCommentToken(tokens, ref currentIndex);
  583. if (null != token && token.Kind == TokenKind.KeywordWith) {
  584. currentIndex++;
  585. token = GetNextNonCommentToken(tokens, ref currentIndex);
  586. if (null != token) {
  587. int matchingParenToken = token.MatchingParenToken;
  588. if (token.Kind == TokenKind.LeftParenthesis && -1 != matchingParenToken && currentIndex < matchingParenToken) {
  589. int endIndex = matchingParenToken;
  590. currentIndex++;
  591. // Make a note if we find any valid table hints
  592. bool foundTableHint = false;
  593. while (currentIndex < endIndex) {
  594. token = GetNextNonCommentToken(tokens, ref currentIndex);
  595. if (null != token) {
  596. if (token.Kind == TokenKind.Comma || token.Kind == TokenKind.LeftParenthesis || token.Kind == TokenKind.RightParenthesis || token.Kind == TokenKind.ValueNumber) {
  597. // Do nothing
  598. } else {
  599. bool foundLocalTableHint = false;
  600. foreach (Token tableHint in staticData.TableHints) {
  601. if (token.Kind == tableHint.Kind) {
  602. foundTableHint = true;
  603. foundLocalTableHint = true;
  604. break;
  605. }
  606. }
  607. if (!foundLocalTableHint) {
  608. return false;
  609. }
  610. }
  611. } else {
  612. return false;
  613. }
  614. currentIndex++;
  615. }
  616. statementEndIndex = endIndex;
  617. return foundTableHint;
  618. }
  619. }
  620. }
  621. return false;
  622. }
  623. /// <summary>
  624. /// Find the start of the Join statement.
  625. /// <table_source> <join_type> <table_source> ON <search_condition>
  626. /// <join_type> ::= [ { CROSS | { INNER | { { LEFT | RIGHT | FULL } [ OUTER ] } } [ <join_hint> ] } ] JOIN
  627. /// <join_hint> ::=
  628. /// { LOOP | HASH | MERGE | REMOTE }
  629. /// </summary>
  630. /// <param name="tokens"></param>
  631. /// <param name="currentIndex">currentIndex points at the JOIN token</param>
  632. /// <param name="startIndex"></param>
  633. /// <returns></returns>
  634. public static bool FindStartOfJoin(List<TokenInfo> tokens, int currentIndex, out int startIndex) {
  635. startIndex = -1;
  636. TokenInfo token = GetPreviousNonCommentToken(tokens, ref currentIndex);
  637. if (null != token && token.Kind == TokenKind.KeywordJoin) {
  638. bool joinHintFound = false;
  639. currentIndex--;
  640. token = GetPreviousNonCommentToken(tokens, ref currentIndex);
  641. if (null != token) {
  642. if (token.Kind == TokenKind.KeywordLoop || token.Kind == TokenKind.KeywordHash || token.Kind == TokenKind.KeywordMerge || token.Kind == TokenKind.KeywordRemote) {
  643. joinHintFound = true;
  644. currentIndex--;
  645. token = GetPreviousNonCommentToken(tokens, ref currentIndex);
  646. } else if (token.Kind == TokenKind.KeywordInner || token.Kind == TokenKind.KeywordCross) {
  647. startIndex = currentIndex;
  648. return true;
  649. }
  650. if (null != token) {
  651. bool requireLeftRightFull = false;
  652. if (token.Kind == TokenKind.KeywordOuter) {
  653. requireLeftRightFull = true;
  654. currentIndex--;
  655. token = GetPreviousNonCommentToken(tokens, ref currentIndex);
  656. }
  657. if (null != token) {
  658. if (token.Kind == TokenKind.KeywordLeft || token.Kind == TokenKind.KeywordRight || token.Kind == TokenKind.KeywordFull) {
  659. startIndex = currentIndex;
  660. return true;
  661. }
  662. }
  663. // Since INNER and OUTER keywords are optional, a join hint may be the start of the Join statement
  664. if (joinHintFound) {
  665. currentIndex++;
  666. GetNextNonCommentToken(tokens, ref currentIndex);
  667. startIndex = currentIndex;
  668. return true;
  669. }
  670. if (!requireLeftRightFull) {
  671. startIndex = currentIndex;
  672. return true;
  673. }
  674. }
  675. }
  676. }
  677. return false;
  678. }
  679. }
  680. }