001/*-------------------------------------------------------------------------+ 002| | 003| Copyright 2005-2011 the ConQAT Project | 004| | 005| Licensed under the Apache License, Version 2.0 (the "License"); | 006| you may not use this file except in compliance with the License. | 007| You may obtain a copy of the License at | 008| | 009| http://www.apache.org/licenses/LICENSE-2.0 | 010| | 011| Unless required by applicable law or agreed to in writing, software | 012| distributed under the License is distributed on an "AS IS" BASIS, | 013| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 014| See the License for the specific language governing permissions and | 015| limitations under the License. | 016+-------------------------------------------------------------------------*/ 017package eu.cqse.check.framework.shallowparser.languages.hanasqlscript; 018 019import static eu.cqse.check.framework.scanner.ETokenType.*; 020import static eu.cqse.check.framework.shallowparser.languages.hanasqlscript.HanaSQLScriptShallowParser.EHanaSQLScriptParserStates.METHOD_STATEMENTS; 021import static eu.cqse.check.framework.shallowparser.languages.hanasqlscript.HanaSQLScriptShallowParser.EHanaSQLScriptParserStates.OUTSIDE_METHODS; 022 023import java.util.EnumSet; 024 025import org.conqat.lib.commons.region.Region; 026 027import eu.cqse.check.framework.scanner.ETokenType; 028import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType; 029import eu.cqse.check.framework.shallowparser.framework.RecognizerBase; 030import eu.cqse.check.framework.shallowparser.framework.ShallowParserBase; 031 032/** 033 * Parser for SAP Hana SQLScript. 034 */ 035public class HanaSQLScriptShallowParser 036 extends ShallowParserBase<HanaSQLScriptShallowParser.EHanaSQLScriptParserStates> { 037 038 /** States common to SQL Parsers. */ 039 public static enum EHanaSQLScriptParserStates { 040 /** 041 * The highest level state. Outside procedures, functions or triggers. 042 * Applies to SQL files without these method structures. 043 */ 044 OUTSIDE_METHODS, 045 046 /** 047 * Simple and block statements inside a trigger, procedure or function. 048 */ 049 METHOD_STATEMENTS 050 } 051 052 /** 053 * In Hana SQLScript language, double-quoted keywords may be used as 054 * identifiers 055 */ 056 private static final EnumSet<ETokenType> HANA_SQLSCRIPT_IDENTIFIERS = EnumSet.of(IDENTIFIER, ABS, ALL, ALLOCATE, 057 ALTER, AND, ANY, ARE, ARRAY, ARRAY_AGG, ARRAY_MAX_CARDINALITY, AS, ASENSITIVE, ASYMMETRIC, AT, ATOMIC, 058 AUTHORIZATION, AVG, BEGIN, BEGIN_FRAME, BEGIN_PARTITION, BETWEEN, BIGINT, BINARY, BLOB, BOOLEAN, BOTH, BY, 059 CALL, CALLED, CARDINALITY, CASCADED, CASE, CAST, CEIL, CEILING, CHAR, CHAR_LENGTH, CHARACTER, 060 CHARACTER_LENGTH, CHECK, CLASSIFIER, CLOB, CLOSE, COALESCE, COLLATE, COLLECT, COLUMN, COMMIT, CONDITION, 061 CONNECT, CONSTRAINT, CONTAINS, CONVERT, CORR, CORRESPONDING, COUNT, COVAR_POP, COVAR_SAMP, CREATE, CROSS, 062 CUBE, CUME_DIST, CURRENT, CURRENT_CATALOG, CURRENT_DATE, CURRENT_DEFAULT_TRANSFORM_GROUP, CURRENT_PATH, 063 CURRENT_ROLE, CURRENT_ROW, CURRENT_SCHEMA, CURRENT_TIME, CURRENT_TIMESTAMP, 064 CURRENT_TRANSFORM_GROUP_FOR_TYPE, CURRENT_USER, CURSOR, CYCLE, DATE, DAY, DEALLOCATE, DEC, DECIMAL, DECLARE, 065 DEFAULT, DEFINE, DELETE, DENSE_RANK, DEREF, DESCRIBE, DETERMINISTIC, DISCONNECT, DISTINCT, DOUBLE, DROP, 066 DYNAMIC, EACH, ELEMENT, ELSE, EMPTY, END, END_FRAME, END_PARTITION, EQUALS, ESCAPE, EVERY, EXCEPT, EXEC, 067 EXECUTE, EXISTS, EXP, EXTERNAL, EXTRACT, FALSE, FETCH, FILTER, FIRST_VALUE, FLOAT, FLOOR, FOR, FOREIGN, 068 FRAME_ROW, FREE, FROM, FULL, FUNCTION, FUSION, GET, GLOBAL, GRANT, GROUP, GROUPING, GROUPS, HAVING, HOLD, 069 HOUR, IDENTITY, IN, INDICATOR, INITIAL, INNER, INOUT, INSENSITIVE, INSERT, INT, INTEGER, INTERSECT, 070 INTERSECTION, INTERVAL, INTO, IS, JOIN, LAG, LANGUAGE, LARGE, LAST_VALUE, LATERAL, LEAD, LEADING, LEFT, 071 LIKE, LIKE_REGEX, LN, LOCAL, LOCALTIME, LOCALTIMESTAMP, LOWER, MATCH, MATCH_NUMBER, MATCH_RECOGNIZE, 072 MATCHES, MAX, MEMBER, MERGE, METHOD, MIN, MINUTE, MOD, MODIFIES, MODULE, MONTH, MULTISET, NATIONAL, NATURAL, 073 NCHAR, NCLOB, NEW, NO, NONE, NORMALIZE, NOT, NTH_VALUE, NTILE, NULL, NULLIF, NUMERIC, OCTET_LENGTH, 074 OCCURRENCES_REGEX, OF, OFFSET, OLD, OMIT, ON, ONE, ONLY, OPEN, OR, ORDER, OUT, OUTER, OVER, OVERLAPS, 075 OVERLAY, PARAMETER, PARTITION, PATTERN, PER, PERCENT, PERCENT_RANK, PERCENTILE_CONT, PERCENTILE_DISC, 076 PERIOD, PORTION, POSITION, POSITION_REGEX, POWER, PRECEDES, PRECISION, PREPARE, PRIMARY, PROCEDURE, RANGE, 077 RANK, READS, REAL, RECOVER, RECURSIVE, REF, REFERENCES, REFERENCING, REGR_AVGX, REGR_AVGY, REGR_COUNT, 078 REGR_INTERCEPT, REGR_R2, REGR_SLOPE, REGR_SXX, REGR_SXY, REGR_SYY, RELEASE, RESULT, RETURN, RETURNS, REVOKE, 079 RIGHT, ROLLBACK, ROLLUP, ROW, ROW_NUMBER, ROWS, RUNNING, SAVEPOINT, SCOPE, SCROLL, SEARCH, SECOND, SEEK, 080 SELECT, SENSITIVE, SESSION_USER, SET, SHOW, SKIP, SIMILAR, SMALLINT, SOME, SPECIFIC, SPECIFICTYPE, SQL, 081 SQLEXCEPTION, SQLSTATE, SQLWARNING, SQRT, START, STATIC, STDDEV_POP, STDDEV_SAMP, SUBMULTISET, SUBSET, 082 SUBSTRING, SUBSTRING_REGEX, SUCCEEDS, SUM, SYMMETRIC, SYSTEM, SYSTEM_TIME, SYSTEM_USER, TABLE, TABLESAMPLE, 083 THEN, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE, TO, TRAILING, TRANSLATE, TRANSLATE_REGEX, 084 TRANSLATION, TREAT, TRIGGER, TRUNCATE, TRIM, TRIM_ARRAY, TRUE, UESCAPE, UNION, UNIQUE, UNKNOWN, UNLOAD, 085 UNNEST, UPDATE, UPPER, UPSERT, USER, USING, VALUE, VALUES, VALUE_OF, VAR_POP, VAR_SAMP, VARBINARY, 086 VARCHAR, VARYING, VERSIONING, WHEN, WHENEVER, WHERE, WIDTH_BUCKET, WINDOW, WITH, WITHIN, WITHOUT, YEAR); 087 088 /** 089 * Hana SQLScript Constructor 090 */ 091 public HanaSQLScriptShallowParser() { 092 super(EHanaSQLScriptParserStates.class, EHanaSQLScriptParserStates.OUTSIDE_METHODS); 093 094 createMetaRules(); 095 createMethodRules(); 096 createStatementRules(); 097 } 098 099 /** 100 * Create parser rules for generating meta shallow entities. Meta entities 101 * do not qualify as statements on their own. 102 */ 103 private void createMetaRules() { 104 // SET Statements ... 105 // ... SET HISTORY SESSION 106 inState(OUTSIDE_METHODS).sequence(SET, HISTORY, SESSION, TO).createNode(EShallowEntityType.META, "set", 1) 107 .skipTo(SEMICOLON).endNode(); 108 // ... session variables 109 RecognizerBase<EHanaSQLScriptParserStates> otherSetStatementsMatch = inState(OUTSIDE_METHODS).sequence(SET, 110 HANA_SQLSCRIPT_IDENTIFIERS); 111 RecognizerBase<EHanaSQLScriptParserStates> sessionVarMatch = otherSetStatementsMatch 112 .repeated(HANA_SQLSCRIPT_IDENTIFIERS).sequence(EQUAL) 113 .createNode(EShallowEntityType.ATTRIBUTE, "session variable", 1); 114 sessionVarMatch.skipTo(SEMICOLON).endNode(); 115 RecognizerBase<EHanaSQLScriptParserStates> schemaAndOtherMatch = otherSetStatementsMatch 116 .repeated(HANA_SQLSCRIPT_IDENTIFIERS).sequence(SEMICOLON).createNode(EShallowEntityType.META, "set", 1); 117 schemaAndOtherMatch.endNode(); 118 119 createRulesForSQLStatements(); 120 } 121 122 /** 123 * Create rules to match SQL statements that may be found in any state. 124 */ 125 private void createRulesForSQLStatements() { 126 // SQL 127 inAnyState() 128 .sequence(EnumSet.of(ALTER, COMMIT, DELETE, GRANT, LOCK, ROLLBACK, SAVEPOINT, SELECT, DROP, MERGE, 129 UPDATE, TRUNCATE, REVOKE, RENAME, LOAD, IMPORT, EXPORT, UPSERT, REPLACE, UNLOAD, BACKUP, 130 RECOVER)) 131 .createNode(EShallowEntityType.STATEMENT, "SQL", 0).skipTo(SEMICOLON).endNode(); 132 // ... INSERT INTO statement 133 RecognizerBase<EHanaSQLScriptParserStates> insertMatch = inAnyState().sequence(INSERT, INTO) 134 .createNode(EShallowEntityType.STATEMENT, "SQL", 0); 135 RecognizerBase<EHanaSQLScriptParserStates> matchInsertTableName = matchTableNameForRule(insertMatch); 136 matchInsertTableName.skipTo(SEMICOLON).endNode(); 137 // ... transactions 138 inAnyState().sequence(SET, TRANSACTION).createNode(EShallowEntityType.STATEMENT, "SQL", 1).skipTo(SEMICOLON) 139 .endNode(); 140 // ... CREATE 141 createRuleForSQLCreate(); 142 } 143 144 /** 145 * Creates rules for SQL CREATE statements. 146 */ 147 private void createRuleForSQLCreate() { 148 RecognizerBase<EHanaSQLScriptParserStates> createMatch = inAnyState().sequence(CREATE); 149 createMatch.sequence(EnumSet.of(FULLTEXT, GRAPH, SCHEMA, SEQUENCE, STATISTICS, VIEW)) 150 .createNode(EShallowEntityType.STATEMENT, "SQL", 0).skipTo(SEMICOLON).endNode(); 151 152 createMatch.optional(PUBLIC).sequence(SYNONYM).createNode(EShallowEntityType.STATEMENT, "SQL", 0) 153 .skipTo(SEMICOLON).endNode(); 154 155 createMatch.optional(UNIQUE).optional(EnumSet.of(BTREE, CPBTREE)).sequence(INDEX) 156 .createNode(EShallowEntityType.STATEMENT, "SQL", 0).skipTo(SEMICOLON).endNode(); 157 158 completeCreateTableRule(createMatch.optional(EnumSet.of(VIRTUAL, ROW, COLUMN))); 159 completeCreateTableRule(createMatch.optional(HISTORY, COLUMN)); 160 completeCreateTableRule(createMatch.optional(EnumSet.of(LOCAL, GLOBAL), TEMPORARY).optional(COLUMN)); 161 162 } 163 164 /** 165 * Completes the rule for matching SQL Create table statements for HANA 166 * SQLScript 167 */ 168 private static void completeCreateTableRule(RecognizerBase<EHanaSQLScriptParserStates> state) { 169 RecognizerBase<EHanaSQLScriptParserStates> matchTable = state.sequence(TABLE) 170 .createNode(EShallowEntityType.STATEMENT, "SQL", 0); 171 RecognizerBase<EHanaSQLScriptParserStates> matchTableName = matchTableNameForRule(matchTable); 172 matchTableName.skipTo(SEMICOLON).endNode(); 173 } 174 175 /** 176 * For SQL Create and INSERT statements, matches the table name taking into 177 * account that temporary tables begin with a hash (#) symbol. 178 */ 179 private static RecognizerBase<EHanaSQLScriptParserStates> matchTableNameForRule( 180 RecognizerBase<EHanaSQLScriptParserStates> state) { 181 return state.optional(ETokenType.HASH).repeated(HANA_SQLSCRIPT_IDENTIFIERS, DOT) 182 .sequence(HANA_SQLSCRIPT_IDENTIFIERS); 183 } 184 185 /** 186 * Create parser rules for generating function and procedure shallow 187 * entities. 188 */ 189 private void createMethodRules() { 190 RecognizerBase<EHanaSQLScriptParserStates> methodStart = inState(OUTSIDE_METHODS).optional(CREATE).markStart() 191 .sequence(EnumSet.of(FUNCTION, PROCEDURE, TRIGGER)).repeated(HANA_SQLSCRIPT_IDENTIFIERS, DOT) 192 .sequence(HANA_SQLSCRIPT_IDENTIFIERS).createNode(EShallowEntityType.METHOD, 0, new Region(1, -1)) 193 .skipNested(LPAREN, RPAREN); 194 RecognizerBase<EHanaSQLScriptParserStates> triggerMatch = methodStart.skipTo(ON).sequence(IDENTIFIER) 195 .skipBefore(BEGIN); 196 completeMethodMatch(triggerMatch); 197 198 RecognizerBase<EHanaSQLScriptParserStates> functionsAndProcsMatch = methodStart.skipBefore(AS).skipBefore(BEGIN) 199 .optional(SEQUENTIAL, EXECUTION); 200 completeMethodMatch(functionsAndProcsMatch); 201 } 202 203 /** 204 * Execute last rule in order to complete the match for method structures: 205 * triggers, functions and procedures. 206 */ 207 private static void completeMethodMatch(RecognizerBase<EHanaSQLScriptParserStates> match) { 208 match.sequence(BEGIN).parseUntil(METHOD_STATEMENTS).sequence(END).optional(SEMICOLON).endNode(); 209 } 210 211 /** 212 * Create parser rules for generating shallow entities for simple and block 213 * statements inside a function or method. 214 */ 215 private void createStatementRules() { 216 createRulesForVariablesConstants(); 217 createRulesForIfElse(); 218 createRulesForCaseElse(); 219 220 // BEGIN...END block 221 RecognizerBase<EHanaSQLScriptParserStates> blockHeadMatch = inState(METHOD_STATEMENTS).sequence(BEGIN) 222 .createNode(EShallowEntityType.STATEMENT, "block", 0); 223 completeAnonymousBlockMatch(blockHeadMatch.optional(AUTONOMOUS, TRANSACTION)); 224 completeAnonymousBlockMatch(blockHeadMatch.optional(PARALLEL, EXECUTION)); 225 226 // Loops 227 createRuleForLoops(WHILE, WHILE); 228 createRuleForLoops(FOR, FOR, IDENTIFIER, EnumSet.of(IN, AS)); 229 230 // Assignment 231 Object assignmentOperators = EnumSet.of(EQUAL, ASSIGNMENT); 232 inState(METHOD_STATEMENTS).repeated(IDENTIFIER, DOT).sequence(IDENTIFIER).optional(LEFT_LABEL_BRACKET) 233 .skipBefore(assignmentOperators).sequence(assignmentOperators) 234 .createNode(EShallowEntityType.STATEMENT, "assignment", 0).skipTo(SEMICOLON).endNode(); 235 236 // Internal procedure calls 237 inState(METHOD_STATEMENTS).sequence(CALL).repeated(HANA_SQLSCRIPT_IDENTIFIERS, DOT).markStart() 238 .sequence(HANA_SQLSCRIPT_IDENTIFIERS, LPAREN).skipTo(RPAREN) 239 .createNode(EShallowEntityType.STATEMENT, "procedure call", 0).endNode(); 240 241 // Other statements 242 inState(METHOD_STATEMENTS) 243 .sequence(EnumSet.of(EXEC, SIGNAL, RESIGNAL, OPEN, CLOSE, BREAK, FETCH, CONTINUE, EXECUTE, RETURN)) 244 .createNode(EShallowEntityType.STATEMENT, 0).skipTo(SEMICOLON).endNode(); 245 } 246 247 /** 248 * Complete match for anonymous block 249 */ 250 private static void completeAnonymousBlockMatch(RecognizerBase<EHanaSQLScriptParserStates> state) { 251 state.parseUntil(METHOD_STATEMENTS).sequence(END).skipTo(SEMICOLON).endNode(); 252 } 253 254 /** 255 * Creates rule for matching FOR and WHILE SQL loop statements. 256 */ 257 private void createRuleForLoops(Object endToken, Object... startTokens) { 258 inState(METHOD_STATEMENTS).sequence(startTokens).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DO) 259 .parseUntil(METHOD_STATEMENTS).sequence(END, endToken, SEMICOLON).endNode(); 260 } 261 262 /** Create rules for matching IF...THEN...ELSE...IF statements */ 263 private void createRulesForIfElse() { 264 // IF...THEN...ELSEIF...ELSE 265 RecognizerBase<EHanaSQLScriptParserStates> ifAlternative = inState(METHOD_STATEMENTS) 266 .sequence(EnumSet.of(IF, ELSEIF)).createNode(EShallowEntityType.STATEMENT, 0) 267 .skipToWithNesting(THEN, CASE, END).parseUntil(METHOD_STATEMENTS) 268 .sequenceBefore(EnumSet.of(ELSEIF, ELSE, END)); 269 ifAlternative.sequence(END, IF, SEMICOLON).endNode(); 270 ifAlternative.endNodeWithContinuation(); 271 272 // ELSE (both for if and case) 273 RecognizerBase<EHanaSQLScriptParserStates> elseMatcher = inState(METHOD_STATEMENTS).sequence(ELSE) 274 .createNode(EShallowEntityType.STATEMENT, 0).parseUntil(METHOD_STATEMENTS); 275 elseMatcher.sequence(END).skipTo(SEMICOLON).endNode(); 276 elseMatcher.sequenceBefore(END, CASE).endNode(); 277 } 278 279 /** Create rules for matching CASE...WHEN...THEN...ELSE statements */ 280 private void createRulesForCaseElse() { 281 // CASE 282 inState(METHOD_STATEMENTS).sequence(CASE, IDENTIFIER).createNode(EShallowEntityType.STATEMENT, 0) 283 .parseUntil(METHOD_STATEMENTS).endNode(); 284 285 // WHEN in case 286 inState(METHOD_STATEMENTS).sequence(WHEN).skipTo(THEN).createNode(EShallowEntityType.STATEMENT, "when", 1) 287 .endNode(); 288 } 289 290 /** Creates rules for matching variables, constants, cursors, e.t.c. */ 291 private void createRulesForVariablesConstants() { 292 RecognizerBase<EHanaSQLScriptParserStates> declareMatch = inState(METHOD_STATEMENTS).sequence(DECLARE); 293 // Cursors. 294 declareMatch.sequence(CURSOR).skipTo(FOR).createNode(EShallowEntityType.ATTRIBUTE, "cursor", 2).sequence(SELECT) 295 .skipTo(SEMICOLON).endNode(); 296 // (exit) Exception handler, with an accompanying select statement or 297 // method block 298 RecognizerBase<EHanaSQLScriptParserStates> exitHandlerMatch = declareMatch.sequence(EXIT, HANDLER, FOR); 299 RecognizerBase<EHanaSQLScriptParserStates> exitHandlerForSQLExceptionAndConditionMatch = exitHandlerMatch 300 .sequence(EnumSet.of(SQLEXCEPTION, IDENTIFIER)); 301 RecognizerBase<EHanaSQLScriptParserStates> exitHandlerForSQLErrorMatch = exitHandlerMatch 302 .sequence(SQL_ERROR_CODE, INTEGER_LITERAL); 303 304 completeRuleForExitHandlerBlocks(exitHandlerForSQLExceptionAndConditionMatch, -2); 305 completeRuleForExitHandlerBlocks(exitHandlerForSQLErrorMatch, -3); 306 307 exitHandlerForSQLExceptionAndConditionMatch.createNode(EShallowEntityType.ATTRIBUTE, "exit handler", -1) 308 .optional(SELECT).skipTo(SEMICOLON).endNode(); 309 exitHandlerForSQLErrorMatch.createNode(EShallowEntityType.ATTRIBUTE, "exit handler", -2).optional(SELECT) 310 .skipTo(SEMICOLON).endNode(); 311 312 // Conditions 313 declareMatch.sequence(IDENTIFIER, CONDITION).optional(FOR, IDENTIFIER) 314 .createNode(EShallowEntityType.ATTRIBUTE, "condition variable", 1).endNode(); 315 // ... simple, constant and condition variable declarations 316 declareMatch.sequence(IDENTIFIER).repeated(HANA_SQLSCRIPT_IDENTIFIERS) 317 .createNode(EShallowEntityType.ATTRIBUTE, "variable", 1).skipTo(SEMICOLON).endNode(); 318 } 319 320 /** 321 * Conclude rule for matching exit handler blocks 322 */ 323 private static void completeRuleForExitHandlerBlocks(RecognizerBase<EHanaSQLScriptParserStates> state, int pos) { 324 state.sequence(BEGIN).createNode(EShallowEntityType.STATEMENT, "exit handler block", pos) 325 .parseUntil(METHOD_STATEMENTS).sequence(END, SEMICOLON).endNode(); 326 } 327 328}