PageRenderTime 32ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/SmarterSql/SmarterSql/Parsing/Keywords/KeywordSelect.cs

#
C# | 366 lines | 280 code | 28 blank | 58 comment | 116 complexity | c00244c91c31701584289ed4ca793e96 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.Expressions;
  8. using Sassner.SmarterSql.Parsing.SelectItems;
  9. using Sassner.SmarterSql.ParsingUtils;
  10. using Sassner.SmarterSql.Tree;
  11. using Sassner.SmarterSql.Utils;
  12. namespace Sassner.SmarterSql.Parsing.Keywords {
  13. public class KeywordSelect {
  14. #region Member variables
  15. private const string ClassName = "KeywordSelect";
  16. #endregion
  17. public static void ParseSelectForColumns(Parser parser, out StatementSpans currentStartSpan, List<SysObject> lstSysObjects, ref int i, List<TokenInfo> lstTokens, ref int sysObjectId) {
  18. List<SysObjectColumn> columns = new List<SysObjectColumn>(50);
  19. List<SelectItem> selectItems = new List<SelectItem>(50);
  20. currentStartSpan = parser.SegmentUtils.GetStatementSpan(i);
  21. if (null == currentStartSpan) {
  22. return;
  23. }
  24. currentStartSpan.Columns = columns;
  25. currentStartSpan.SelectItems = selectItems;
  26. //SELECT [ ALL | DISTINCT ]
  27. //[ TOP expression [ PERCENT ] [ WITH TIES ] ]
  28. //<select_list>
  29. try {
  30. int endIndex = currentStartSpan.IntoIndex;
  31. if (-1 == endIndex) {
  32. endIndex = currentStartSpan.FromIndex;
  33. }
  34. if (-1 == endIndex) {
  35. endIndex = currentStartSpan.EndIndex;
  36. }
  37. TokenInfo token;
  38. i--;
  39. if (!InStatement.GetIfAllNextValidToken(lstTokens, ref i, out token, TokenKind.KeywordSelect)) {
  40. i++;
  41. return;
  42. }
  43. // [ ALL | DISTINCT ]
  44. InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out token, TokenKind.KeywordAll, TokenKind.KeywordDistinct);
  45. // [ TOP expression [ PERCENT ] [ WITH TIES ] ]
  46. if (InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out token, TokenKind.KeywordTop)) {
  47. Expression expression;
  48. i++;
  49. if (!Expression.FindExpression(parser, lstTokens, ref i, endIndex, out expression)) {
  50. return;
  51. }
  52. InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out token, TokenKind.KeywordPercent);
  53. InStatement.GetIfAllNextValidToken(lstTokens, ref i, out token, TokenKind.KeywordWith, TokenKind.KeywordTies);
  54. }
  55. int startIndex = i + 1;
  56. // Validate start and end index. DECLARE cursor might move the start index some
  57. if ((startIndex < i && lstTokens[currentStartSpan.StartIndex].Kind != TokenKind.KeywordDeclare) || i > endIndex) {
  58. i = endIndex;
  59. return;
  60. }
  61. ParseSelectList(parser, lstTokens, ref startIndex, endIndex, currentStartSpan, columns, selectItems);
  62. int offset = startIndex;
  63. token = InStatement.GetNextNonCommentToken(lstTokens, ref offset);
  64. int indexSelectInto = -1;
  65. if (null != token && token.Kind == TokenKind.KeywordInto) {
  66. offset++;
  67. token = InStatement.GetNextNonCommentToken(lstTokens, ref offset);
  68. if (null != token && token.Type == TokenType.Identifier) {
  69. token.TokenContextType = TokenContextType.TempTable;
  70. if (token.Token.UnqoutedImage.StartsWith("#")) {
  71. indexSelectInto = offset;
  72. }
  73. }
  74. }
  75. endIndex = currentStartSpan.EndIndex;
  76. if (startIndex < endIndex) {
  77. // Parse rest of SELECT statement
  78. parser.ParseTokenRange(currentStartSpan, startIndex, endIndex, lstSysObjects);
  79. }
  80. // Find tables that are possible in this segment. Must be done after the parser.ParseTokenRange() statement
  81. List<TableSource> foundTableSources;
  82. if (parser.SegmentUtils.GetUniqueStatementSpanTablesources(currentStartSpan, out foundTableSources, false)) {
  83. foreach (SelectItem selectItem in selectItems) {
  84. if (selectItem is SelectItemStar) {
  85. // Include all columns from all table sources
  86. foreach (TableSource tableSource in foundTableSources) {
  87. foreach (SysObjectColumn column in tableSource.Table.SysObject.Columns) {
  88. SysObjectColumn col = new SysObjectColumn(null, column.ColumnName, column.ParsedDataType, false, false);
  89. selectItem.AddSysObjectColumn(col);
  90. columns.Add(col);
  91. }
  92. }
  93. } else if (selectItem is SelectItemSysObjectStar) {
  94. SelectItemSysObjectStar selectItemSysObjectStar = (SelectItemSysObjectStar)selectItem;
  95. foreach (TableSource tableSource in foundTableSources) {
  96. if (tableSource.Table.TableName.Equals(selectItemSysObjectStar.Token.Token.UnqoutedImage, StringComparison.OrdinalIgnoreCase) || tableSource.Table.Alias.Equals(selectItemSysObjectStar.Token.Token.UnqoutedImage, StringComparison.OrdinalIgnoreCase)) {
  97. foreach (SysObjectColumn column in tableSource.Table.SysObject.Columns) {
  98. SysObjectColumn col = new SysObjectColumn(null, column.ColumnName, column.ParsedDataType, false, false);
  99. selectItem.AddSysObjectColumn(col);
  100. columns.Add(col);
  101. }
  102. break;
  103. }
  104. }
  105. } else if (selectItem is SelectItemExpression) {
  106. SelectItemExpression selectItemExpression = (SelectItemExpression)selectItem;
  107. Connection connection = Instance.TextEditor.ActiveConnection;
  108. ParsedDataType parsedDataType = Expression.GetParsedDataType(connection, lstSysObjects, foundTableSources, selectItemExpression.Expressions);
  109. foreach (SysObjectColumn sysObjectColumn in selectItemExpression.SysObjectColumns) {
  110. if (null != sysObjectColumn) {
  111. sysObjectColumn.ParsedDataType = parsedDataType;
  112. }
  113. }
  114. }
  115. }
  116. }
  117. // Remove duplicates
  118. for (int k = columns.Count - 1; k >= 0; k--) {
  119. for (int j = k - 1; j >= 0; j--) {
  120. if (columns[k].ColumnName.Equals(columns[j].ColumnName, StringComparison.OrdinalIgnoreCase)) {
  121. columns.RemoveAt(k);
  122. break;
  123. }
  124. }
  125. }
  126. // Did a SELECT..INTO statment exists? If so, create a temporary table
  127. if (-1 != indexSelectInto) {
  128. token = lstTokens[indexSelectInto];
  129. // Create a SysObject object
  130. string tableName = token.Token.UnqoutedImage;
  131. SysObject addedSysObject = SysObject.CreateTemporarySysObject(tableName, Common.enSqlTypes.Temporary, ref sysObjectId);
  132. lstSysObjects.Add(addedSysObject);
  133. foreach (SysObjectColumn sysObjectColumn in columns) {
  134. SysObjectColumn tempSysObjectColumn = new SysObjectColumn(addedSysObject, sysObjectColumn.ColumnName, sysObjectColumn.ParsedDataType, false, false);
  135. addedSysObject.AddColumn(tempSysObjectColumn);
  136. }
  137. // Create the table source
  138. Table table = new Table("", "", "", tableName, "", addedSysObject, currentStartSpan, indexSelectInto, indexSelectInto, true);
  139. TableSource addedTableSource = new TableSource(lstTokens[indexSelectInto - 1].Token, table, indexSelectInto, indexSelectInto);
  140. parser.TableSources.Add(addedTableSource);
  141. currentStartSpan.AddTableSource(addedTableSource);
  142. }
  143. i = endIndex;
  144. } catch (Exception e) {
  145. Common.LogEntry(ClassName, "ParseSelectForColumns", e, Common.enErrorLvl.Error);
  146. }
  147. }
  148. //<select_list> ::=
  149. // {
  150. //* *
  151. //* | { table_name | view_name | table_alias }.*
  152. //* | [ { table_name | view_name | table_alias }. ] { column_name | $IDENTITY | $ROWGUID }
  153. //* | column_alias = expression
  154. //* | expression [ [ AS ] column_alias ]
  155. // | udt_column_name [ { . | :: } { { property_name | field_name } | method_name ( argument [ ,...n] ) } ]
  156. // } [ ,...n ]
  157. private static bool ParseSelectList(Parser parser, List<TokenInfo> lstTokens, ref int startIndex, int endIndex, StatementSpans currentStartSpan, List<SysObjectColumn> columns, List<SelectItem> selectItems) {
  158. TokenInfo token;
  159. while (startIndex <= endIndex) {
  160. token = InStatement.GetNextNonCommentToken(lstTokens, ref startIndex);
  161. int offset = startIndex + 1;
  162. TokenInfo nextToken = InStatement.GetNextNonCommentToken(lstTokens, offset);
  163. if (null == token) {
  164. return false;
  165. }
  166. SelectItem selectItem = null;
  167. bool tokenHandled = false;
  168. if (token.Kind == TokenKind.KeywordFrom || token.Kind == TokenKind.KeywordInto) {
  169. break;
  170. }
  171. if (token.Kind == TokenKind.Multiply) {
  172. // *
  173. tokenHandled = true;
  174. selectItem = new SelectItemStar(startIndex);
  175. } else if (Common.IsIdentifier(token, nextToken)) {
  176. offset++;
  177. TokenInfo nextNextToken = InStatement.GetNextNonCommentToken(lstTokens, ref offset);
  178. if (null != nextToken && nextToken.Kind == TokenKind.Dot && null != nextNextToken && nextNextToken.Kind == TokenKind.Multiply) {
  179. // { table_name | view_name | table_alias }.*
  180. // TODO: $IDENTITY | $ROWGUID
  181. // TODO: Skilj på SysObjectAlias och SysObject
  182. token.TokenContextType = TokenContextType.SysObjectAlias;
  183. selectItem = new SelectItemSysObjectStar(token, startIndex, offset);
  184. startIndex = offset;
  185. tokenHandled = true;
  186. } else if (null != nextToken && nextToken.Kind == TokenKind.Assign) {
  187. ParseAssignAliasToExpression(ref startIndex, endIndex, lstTokens, offset, ref token, parser, currentStartSpan, columns, out selectItem, out tokenHandled);
  188. }
  189. }
  190. if (!tokenHandled) {
  191. ParseAssignExpressionToAlias(ref startIndex, endIndex, lstTokens, token, parser, currentStartSpan, columns, out selectItem);
  192. }
  193. if (null != selectItem) {
  194. selectItems.Add(selectItem);
  195. }
  196. offset = startIndex + 1;
  197. token = InStatement.GetNextNonCommentToken(lstTokens, ref offset);
  198. if (null == token || offset > endIndex) {
  199. break;
  200. }
  201. startIndex = offset;
  202. }
  203. return true;
  204. }
  205. /// <summary>
  206. /// expression [ [ AS ] column_alias ]
  207. /// </summary>
  208. /// <param name="startIndex"></param>
  209. /// <param name="endIndex"></param>
  210. /// <param name="lstTokens"></param>
  211. /// <param name="token"></param>
  212. /// <param name="parser"></param>
  213. /// <param name="currentStartSpan"></param>
  214. /// <param name="columns"></param>
  215. /// <param name="selectItem"></param>
  216. private static bool ParseAssignExpressionToAlias(ref int startIndex, int endIndex, List<TokenInfo> lstTokens, TokenInfo token, Parser parser, StatementSpans currentStartSpan, List<SysObjectColumn> columns, out SelectItem selectItem) {
  217. string columnName = null;
  218. List<Expression> expressions = new List<Expression>();
  219. SysObjectColumn sysObjectColumn = null;
  220. int startIndexExpression = startIndex;
  221. // expression [ [ AS ] column_alias ]
  222. while (null != token) {
  223. Expression expression;
  224. if (!Expression.FindExpression(parser, lstTokens, ref startIndex, endIndex, out expression) || null == expression) {
  225. break;
  226. }
  227. expressions.Add(expression);
  228. int offset = startIndex + 1;
  229. token = InStatement.GetNextNonCommentToken(lstTokens, ref offset);
  230. if (offset >= endIndex || token.Kind == TokenKind.Comma || token.Kind == TokenKind.KeywordAs) {
  231. break;
  232. }
  233. startIndex++;
  234. }
  235. if (expressions.Count > 0) {
  236. ParsedDataType parsedDataType = null;
  237. TokenInfo tokenColumnAlias = null;
  238. int tokenAliasIndex = 0;
  239. bool hasColumnAlias = (null != token && token.Kind == TokenKind.KeywordAs);
  240. if (hasColumnAlias) {
  241. startIndex += 2;
  242. tokenColumnAlias = InStatement.GetNextNonCommentToken(lstTokens, ref startIndex);
  243. tokenAliasIndex = startIndex;
  244. } else {
  245. if (expressions.Count > 1) {
  246. ColumnExpression lastExpression = expressions[expressions.Count - 1] as ColumnExpression;
  247. if (null != lastExpression && null == lastExpression.Alias) {
  248. hasColumnAlias = true;
  249. tokenColumnAlias = lastExpression.Column;
  250. tokenAliasIndex = lastExpression.EndIndex;
  251. expressions.RemoveAt(expressions.Count - 1);
  252. }
  253. }
  254. ColumnExpression firstExpression = expressions[0] as ColumnExpression;
  255. if (null != firstExpression) {
  256. columnName = firstExpression.Column.Token.UnqoutedImage;
  257. }
  258. }
  259. if (hasColumnAlias) {
  260. columnName = tokenColumnAlias.Token.UnqoutedImage;
  261. parser.DeclaredColumnAliases.Add(new ColumnAlias(columnName, tokenAliasIndex, currentStartSpan));
  262. }
  263. foreach (Expression expression in expressions) {
  264. if (null != expression.ParsedDataType) {
  265. parsedDataType = expression.ParsedDataType;
  266. break;
  267. }
  268. }
  269. if (null != columnName) {
  270. sysObjectColumn = new SysObjectColumn(null, columnName, parsedDataType, false, false);
  271. columns.Add(sysObjectColumn);
  272. }
  273. selectItem = new SelectItemExpression(startIndexExpression, startIndex, columnName, false, expressions);
  274. selectItem.AddSysObjectColumn(sysObjectColumn);
  275. return true;
  276. }
  277. selectItem = null;
  278. return false;
  279. }
  280. /// <summary>
  281. /// @variable = expression
  282. /// column_alias = expression
  283. /// </summary>
  284. /// <param name="startIndex"></param>
  285. /// <param name="endIndex"></param>
  286. /// <param name="lstTokens"></param>
  287. /// <param name="offset"></param>
  288. /// <param name="token"></param>
  289. /// <param name="parser"></param>
  290. /// <param name="currentStartSpan"></param>
  291. /// <param name="columns"></param>
  292. /// <param name="selectItem"></param>
  293. /// <param name="tokenHandled"></param>
  294. private static void ParseAssignAliasToExpression(ref int startIndex, int endIndex, List<TokenInfo> lstTokens, int offset, ref TokenInfo token, Parser parser, StatementSpans currentStartSpan, List<SysObjectColumn> columns, out SelectItem selectItem, out bool tokenHandled) {
  295. string columnName = "";
  296. if (token.Kind == TokenKind.Variable) {
  297. // @variable = expression
  298. string variableName = token.Token.UnqoutedImage;
  299. LocalVariable localVariable = LocalVariable.GetLocalVariable(parser, variableName, startIndex);
  300. if (null != localVariable) {
  301. token.TokenContextType = TokenContextType.Variable;
  302. parser.CalledLocalVariables.Add(new LocalVariable(variableName, startIndex, localVariable.ParsedDataType));
  303. }
  304. } else {
  305. // column_alias = expression
  306. columnName = token.Token.UnqoutedImage;
  307. token.TokenContextType = TokenContextType.ColumnAlias;
  308. parser.DeclaredColumnAliases.Add(new ColumnAlias(columnName, startIndex, currentStartSpan));
  309. }
  310. startIndex = offset;
  311. int startIndexExpression = startIndex;
  312. List<Expression> expressions = new List<Expression>(50);
  313. SysObjectColumn sysObjectColumn = null;
  314. token = InStatement.GetNextNonCommentToken(lstTokens, ref startIndex);
  315. while (null != token) {
  316. Expression expression;
  317. if (!Expression.FindExpression(parser, lstTokens, ref startIndex, endIndex, out expression) || null == expression) {
  318. break;
  319. }
  320. expressions.Add(expression);
  321. offset = startIndex + 1;
  322. token = InStatement.GetNextNonCommentToken(lstTokens, ref offset);
  323. if (startIndex >= endIndex || token.Kind == TokenKind.Comma || token.Kind == TokenKind.KeywordFrom || token.Kind == TokenKind.KeywordInto) {
  324. break;
  325. }
  326. startIndex++;
  327. }
  328. if (expressions.Count > 0 && null != columnName) {
  329. // data type will be set later on
  330. sysObjectColumn = new SysObjectColumn(null, columnName, null, false, false);
  331. columns.Add(sysObjectColumn);
  332. }
  333. tokenHandled = true;
  334. selectItem = new SelectItemExpression(startIndexExpression, startIndex, columnName, true, expressions);
  335. selectItem.AddSysObjectColumn(sysObjectColumn);
  336. }
  337. }
  338. }