PageRenderTime 26ms CodeModel.GetById 30ms RepoModel.GetById 0ms app.codeStats 0ms

/SmarterSql/SmarterSql/Parsing/Keywords/KeywordTrigger.cs

#
C# | 301 lines | 211 code | 40 blank | 50 comment | 86 complexity | ebc7467ab32939a3c73bb8ee87b529b1 MD5 | raw file
  1. // ---------------------------------
  2. // SmarterSql (c) Johan Sassner 2008
  3. // ---------------------------------
  4. using System.Collections.Generic;
  5. using Sassner.SmarterSql.Objects;
  6. using Sassner.SmarterSql.ParsingObjects;
  7. using Sassner.SmarterSql.ParsingUtils;
  8. using Sassner.SmarterSql.Tree;
  9. using Sassner.SmarterSql.Utils;
  10. namespace Sassner.SmarterSql.Parsing.Keywords {
  11. public class KeywordTrigger {
  12. #region HandleEnableDisableTrigger
  13. // ENABLE TRIGGER { [ schema_name . ] trigger_name [ ,...n ] | ALL }
  14. // ON { object_name | DATABASE | ALL SERVER } [ ; ]
  15. //
  16. // DISABLE TRIGGER { [ schema . ] trigger_name [ ,...n ] | ALL }
  17. // ON { object_name | DATABASE | ALL SERVER } [ ; ]
  18. public static void HandleEnableDisableTrigger(List<TokenInfo> lstTokens, ref int i) {
  19. i++;
  20. TokenInfo nextToken = InStatement.GetNextNonCommentToken(lstTokens, ref i);
  21. if (null == nextToken) {
  22. return;
  23. }
  24. if (nextToken.Kind == TokenKind.KeywordAll) {
  25. // Ok
  26. } else if (nextToken.Type == TokenType.Identifier) {
  27. // [ schema_name . ] trigger_name [ ,...n ]
  28. int offset = i - 1;
  29. while (true) {
  30. offset++;
  31. nextToken = InStatement.GetNextNonCommentToken(lstTokens, ref offset);
  32. offset++;
  33. TokenInfo nextNextToken = InStatement.GetNextNonCommentToken(lstTokens, ref offset);
  34. if (null != nextNextToken && nextNextToken.Kind == TokenKind.Dot) {
  35. offset++;
  36. nextNextToken = InStatement.GetNextNonCommentToken(lstTokens, ref offset);
  37. if (null != nextNextToken && nextNextToken.Type == TokenType.Identifier) {
  38. nextToken.TokenContextType = TokenContextType.SysObjectSchema;
  39. nextNextToken.TokenContextType = TokenContextType.Trigger;
  40. i = offset;
  41. } else {
  42. return;
  43. }
  44. } else {
  45. nextToken.TokenContextType = TokenContextType.Trigger;
  46. }
  47. offset++;
  48. nextToken = InStatement.GetNextNonCommentToken(lstTokens, ref offset);
  49. if (null == nextToken || nextToken.Kind != TokenKind.Comma) {
  50. break;
  51. }
  52. i = offset;
  53. }
  54. } else {
  55. return;
  56. }
  57. // ON
  58. if (!InStatement.GetIfAllNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordOn)) {
  59. return;
  60. }
  61. // { object_name | DATABASE | ALL SERVER }
  62. Parser parser = TextEditor.CurrentWindowData.Parser;
  63. if (InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordAll, TokenKind.KeywordDatabase)) {
  64. if (nextToken.Kind == TokenKind.KeywordAll && !InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordServer)) {
  65. return;
  66. }
  67. } else if (null != nextToken && nextToken.Type == TokenType.Identifier) {
  68. // Normal multi part name
  69. TokenInfo server_name;
  70. TokenInfo database_name;
  71. TokenInfo schema_name;
  72. TokenInfo object_name;
  73. int endTableIndex;
  74. if (!parser.ParseTableOrViewName(i, out endTableIndex, out server_name, out database_name, out schema_name, out object_name)) {
  75. return;
  76. }
  77. }
  78. }
  79. #endregion
  80. #region Parse CREATE/ALTER TRIGGER
  81. // --- Trigger on an INSERT, UPDATE, or DELETE statement to a table or view (DML Trigger) ---------
  82. //CREATE TRIGGER [ schema_name . ]trigger_name
  83. // ON { table | view }
  84. //[ WITH <dml_trigger_option> [ ,...n ] ]
  85. //{ FOR | AFTER | INSTEAD OF }
  86. //{ [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] }
  87. //[ WITH APPEND ]
  88. //[ NOT FOR REPLICATION ]
  89. //AS { sql_statement [ ; ] [ ...n ] | EXTERNAL NAME <method specifier [ ; ] > }
  90. //
  91. // ---- Trigger on a CREATE, ALTER, DROP, GRANT, DENY, REVOKE, or UPDATE STATISTICS statement (DDL Trigger) --------
  92. //CREATE TRIGGER trigger_name
  93. //ON { ALL SERVER | DATABASE }
  94. //[ WITH <ddl_trigger_option> [ ,...n ] ]
  95. //{ FOR | AFTER } { event_type | event_group } [ ,...n ]
  96. //AS { sql_statement [ ; ] [ ...n ] | EXTERNAL NAME < method specifier > [ ; ] }
  97. //
  98. //<dml_trigger_option> ::=
  99. // [ ENCRYPTION ]
  100. // [ EXECUTE AS Clause ]
  101. //
  102. public static void HandleCreateAlterTrigger(StatementSpans currentStartSpan, List<TokenInfo> lstTokens, ref int i, ref int sysObjectId, List<SysObject> lstSysObjects) {
  103. Parser parser = TextEditor.CurrentWindowData.Parser;
  104. i++;
  105. TokenInfo nextToken = InStatement.GetNextNonCommentToken(lstTokens, ref i);
  106. if (null == nextToken || nextToken.Type != TokenType.Identifier) {
  107. return;
  108. }
  109. int index = i + 1;
  110. TokenInfo nextNextToken = InStatement.GetNextNonCommentToken(lstTokens, ref index);
  111. if (null != nextNextToken && nextNextToken.Kind == TokenKind.Dot) {
  112. index++;
  113. nextNextToken = InStatement.GetNextNonCommentToken(lstTokens, ref index);
  114. if (null != nextNextToken && nextNextToken.Type == TokenType.Identifier) {
  115. nextToken.TokenContextType = TokenContextType.SysObjectSchema;
  116. nextNextToken.TokenContextType = TokenContextType.Trigger;
  117. i = index;
  118. } else {
  119. return;
  120. }
  121. } else {
  122. nextToken.TokenContextType = TokenContextType.Trigger;
  123. }
  124. if (!InStatement.GetIfAllNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordOn)) {
  125. return;
  126. }
  127. // DDL Trigger
  128. if (InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordAll, TokenKind.KeywordDatabase)) {
  129. #region DDL Trigger
  130. bool serverScope = (nextToken.Kind == TokenKind.KeywordAll);
  131. if (nextToken.Kind == TokenKind.KeywordAll && !InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordServer)) {
  132. return;
  133. }
  134. if (!ParseDmlTrigger(ref i, lstTokens)) {
  135. return;
  136. }
  137. // { FOR | AFTER }
  138. if (!InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordFor, TokenKind.KeywordAfter)) {
  139. return;
  140. }
  141. // { event_type | event_group } [ ,...n ]
  142. int offset = i;
  143. while (true) {
  144. offset++;
  145. TokenInfo token = InStatement.GetNextNonCommentToken(lstTokens, ref offset);
  146. Token foundToken;
  147. if (serverScope) {
  148. if (!Instance.StaticData.DdlStatementsServerScope.TryGetValue(token.Token, out foundToken)) {
  149. token.TokenContextType = TokenContextType.Unknown;
  150. return;
  151. }
  152. } else {
  153. if (!Instance.StaticData.DdlStatementsDatabaseScope.TryGetValue(token.Token, out foundToken)) {
  154. token.TokenContextType = TokenContextType.Unknown;
  155. return;
  156. }
  157. }
  158. if (!InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.Comma)) {
  159. break;
  160. }
  161. }
  162. i = offset;
  163. // AS
  164. #endregion
  165. } else {
  166. #region DML Trigger
  167. int startTableIndex = i + 1;
  168. TokenInfo server_name;
  169. TokenInfo database_name;
  170. TokenInfo schema_name;
  171. TokenInfo object_name;
  172. int endTableIndex;
  173. if (!parser.ParseTableOrViewName(startTableIndex, out endTableIndex, out server_name, out database_name, out schema_name, out object_name)) {
  174. return;
  175. }
  176. i = endTableIndex;
  177. // Normal multi part name
  178. if (!ParseDmlTrigger(ref i, lstTokens)) {
  179. return;
  180. }
  181. // { FOR | AFTER | INSTEAD OF }
  182. if (!InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordFor, TokenKind.KeywordAfter, TokenKind.KeywordInstead)) {
  183. return;
  184. }
  185. if (nextToken.Kind == TokenKind.KeywordInstead && !InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordOf)) {
  186. return;
  187. }
  188. // { [ INSERT ] [ , ] [ UPDATE ] [ , ] [ DELETE ] }
  189. while (true) {
  190. if (!InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordInsert, TokenKind.KeywordUpdate, TokenKind.KeywordDelete)) {
  191. return;
  192. }
  193. if (!InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.Comma)) {
  194. break;
  195. }
  196. }
  197. // [ WITH APPEND ]
  198. InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordWith, TokenKind.KeywordAppend);
  199. // [ NOT FOR REPLICATION ]
  200. InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordNot, TokenKind.KeywordFor, TokenKind.KeywordReplication);
  201. // AS
  202. // Create insert / delete logical (conceptual) tables
  203. SysObject foundSysObject;
  204. if (!Instance.TextEditor.ActiveConnection.SysObjectExists(schema_name, object_name, out foundSysObject)) {
  205. return;
  206. }
  207. CreateLogicalTable(lstTokens, parser, schema_name, "inserted", string.Empty, ref sysObjectId, lstSysObjects, foundSysObject, startTableIndex, endTableIndex, currentStartSpan);
  208. CreateLogicalTable(lstTokens, parser, schema_name, "deleted", string.Empty, ref sysObjectId, lstSysObjects, foundSysObject, startTableIndex, endTableIndex, currentStartSpan);
  209. #endregion
  210. }
  211. }
  212. private static void CreateLogicalTable(List<TokenInfo> lstTokens, Parser parser, TokenInfo schema_name, string Table, string Alias, ref int sysObjectId, List<SysObject> lstSysObjects, SysObject foundSysObject, int startTableIndex, int endTableIndex, StatementSpans currentStartSpan) {
  213. const Common.enSqlTypes sqlType = Common.enSqlTypes.Temporary;
  214. SysObject addedSysObject = SysObject.CreateTemporarySysObject(Table, sqlType, ref sysObjectId);
  215. lstSysObjects.Add(addedSysObject);
  216. foreach (SysObjectColumn column in foundSysObject.Columns) {
  217. addedSysObject.AddColumn(new SysObjectColumn(addedSysObject, column.ColumnName, column.ParsedDataType, column.IsNullable, column.IsIdentity));
  218. }
  219. // Create the table source
  220. StatementSpans ss = parser.SegmentUtils.GetStatementSpan(startTableIndex);
  221. Table table = new Table(string.Empty, string.Empty, (null != schema_name ? schema_name.Token.UnqoutedImage : string.Empty), Table, Alias, addedSysObject, ss, startTableIndex, endTableIndex, true);
  222. TableSource addedTableSource = new TableSource(lstTokens[startTableIndex].Token, table, startTableIndex, endTableIndex);
  223. parser.TableSources.Add(addedTableSource);
  224. if (null != currentStartSpan) {
  225. currentStartSpan.AddTableSource(addedTableSource);
  226. }
  227. }
  228. #endregion
  229. #region ParseDmlTrigger
  230. // [ WITH <dml_trigger_option> [ ,...n ] ]
  231. // <dml_trigger_option> ::=
  232. // [ ENCRYPTION ]
  233. // [ EXECUTE AS Clause ]
  234. private static bool ParseDmlTrigger(ref int i, List<TokenInfo> lstTokens) {
  235. TokenInfo nextToken;
  236. if (InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordWith)) {
  237. while (true) {
  238. if (!InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordEncryption, TokenKind.KeywordExec)) {
  239. return false;
  240. }
  241. if (nextToken.Kind == TokenKind.KeywordExec) {
  242. if (!InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordAs)) {
  243. return false;
  244. }
  245. if (!InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.KeywordCaller, TokenKind.KeywordSelf, TokenKind.KeywordOwner, TokenKind.ValueString)) {
  246. return false;
  247. }
  248. }
  249. if (!InStatement.GetIfAnyNextValidToken(lstTokens, ref i, out nextToken, TokenKind.Comma)) {
  250. if (null == nextToken) {
  251. return false;
  252. }
  253. break;
  254. }
  255. }
  256. }
  257. return true;
  258. }
  259. #endregion
  260. }
  261. }