eu.cqse.check.framework.shallowparser.languages.hanasqlscript.HanaSQLScriptShallowParser Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of teamscale-check-api Show documentation
Show all versions of teamscale-check-api Show documentation
The Teamscale Custom Check API allows users to extend Teamscale by writing custom analyses that create findings.
/*
* Copyright (c) CQSE GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.cqse.check.framework.shallowparser.languages.hanasqlscript;
import static eu.cqse.check.framework.scanner.ETokenType.*;
import static eu.cqse.check.framework.shallowparser.languages.hanasqlscript.HanaSQLScriptShallowParser.EHanaSQLScriptParserStates.METHOD_STATEMENTS;
import static eu.cqse.check.framework.shallowparser.languages.hanasqlscript.HanaSQLScriptShallowParser.EHanaSQLScriptParserStates.OUTSIDE_METHODS;
import java.util.EnumSet;
import org.conqat.lib.commons.region.Region;
import eu.cqse.check.framework.scanner.ETokenType;
import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType;
import eu.cqse.check.framework.shallowparser.framework.RecognizerBase;
import eu.cqse.check.framework.shallowparser.framework.ShallowParserBase;
/**
* Parser for SAP Hana SQLScript.
*/
public class HanaSQLScriptShallowParser
extends ShallowParserBase {
/** States common to SQL Parsers. */
public static enum EHanaSQLScriptParserStates {
/**
* The highest level state. Outside procedures, functions or triggers. Applies to SQL files without
* these method structures.
*/
OUTSIDE_METHODS,
/**
* Simple and block statements inside a trigger, procedure or function.
*/
METHOD_STATEMENTS
}
/**
* In Hana SQLScript language, double-quoted keywords may be used as identifiers
*/
private static final EnumSet HANA_SQLSCRIPT_IDENTIFIERS = EnumSet.of(IDENTIFIER, ABS, ALL, ALLOCATE,
ALTER, AND, ANY, ARE, ARRAY, ARRAY_AGG, ARRAY_MAX_CARDINALITY, AS, ASENSITIVE, ASYMMETRIC, AT, ATOMIC,
AUTHORIZATION, AUTO_CORR, AVG, BEGIN, BEGIN_FRAME, BEGIN_PARTITION, BETWEEN, BIGINT, BINARY, BLOB, BOOLEAN,
BOTH, BY, CALL, CALLED, CARDINALITY, CASCADED, CASE, CAST, CEIL, CEILING, CHAR, CHAR_LENGTH, CHARACTER,
CHARACTER_LENGTH, CHECK, CLASSIFIER, CLOB, CLOSE, COALESCE, COLLATE, COLLECT, COLUMN, COMMIT, CONDITION,
CONNECT, CONSTRAINT, CONTAINS, CONVERT, CORR, CORRESPONDING, CORR_SPEARMAN, COUNT, COVAR_POP, COVAR_SAMP,
CREATE, CROSS, CROSS_CORR, CUBE, CUME_DIST, CURRENT, CURRENT_CATALOG, CURRENT_DATE,
CURRENT_DEFAULT_TRANSFORM_GROUP, CURRENT_PATH, CURRENT_ROLE, CURRENT_ROW, CURRENT_SCHEMA, CURRENT_TIME,
CURRENT_TIMESTAMP, CURRENT_TRANSFORM_GROUP_FOR_TYPE, CURRENT_USER, CURSOR, CYCLE, DATE, DAY, DEALLOCATE,
DEC, DECIMAL, DECLARE, DEFAULT, DEFINE, DELETE, DENSE_RANK, DEREF, DESCRIBE, DETERMINISTIC, DISCONNECT, DFT,
DISTINCT, DOUBLE, DROP, DYNAMIC, EACH, ELEMENT, ELSE, EMPTY, END, END_FRAME, END_PARTITION, EQUALS, ESCAPE,
EVERY, EXCEPT, EXEC, EXECUTE, EXISTS, EXP, EXTERNAL, EXTRACT, FALSE, FETCH, FILTER, FIRST_VALUE, FLOAT,
FLOOR, FOR, FOREIGN, FRAME_ROW, FREE, FROM, FULL, FUNCTION, FUSION, GET, GLOBAL, GRANT, GROUP, GROUPING,
GROUPS, HAVING, HOLD, HOUR, IDENTITY, IN, INDICATOR, INITIAL, INNER, INOUT, INSENSITIVE, INSERT, INT,
INTEGER, INTERSECT, INTERSECTION, INTERVAL, INTO, IS, JOIN, LAG, LANGUAGE, LARGE, LAST_VALUE, LATERAL, LEAD,
LEADING, LEFT, LIKE, LIKE_REGEX, LN, LOCAL, LOCALTIME, LOCALTIMESTAMP, LOWER, MATCH, MATCH_NUMBER,
MATCH_RECOGNIZE, MATCHES, MAX, MEDIAN, MEMBER, MERGE, METHOD, MIN, MINUTE, MOD, MODIFIES, MODULE, MONTH,
MULTISET, NATIONAL, NATURAL, NCHAR, NCLOB, NEW, NO, NONE, NORMALIZE, NOT, NTH_VALUE, NTILE, NULL, NULLIF,
NUMERIC, OCTET_LENGTH, OCCURRENCES_REGEX, OF, OFFSET, OLD, OMIT, ON, ONE, ONLY, OPEN, OR, ORDER, OUT, OUTER,
OVER, OVERLAPS, OVERLAY, PARAMETER, PARTITION, PATTERN, PER, PERCENT, PERCENT_RANK, PERCENTILE_CONT,
PERCENTILE_DISC, PERIOD, PORTION, POSITION, POSITION_REGEX, POWER, PRECEDES, PRECISION, PREPARE, PRIMARY,
PROCEDURE, RANGE, RANK, READS, REAL, RECOVER, RECURSIVE, REF, REFERENCES, REFERENCING, REGR_AVGX, REGR_AVGY,
REGR_COUNT, REGR_INTERCEPT, REGR_R2, REGR_SLOPE, REGR_SXX, REGR_SXY, REGR_SYY, RELEASE, RESULT, RETURN,
RETURNS, REVOKE, RIGHT, ROLLBACK, ROLLUP, ROW, ROW_NUMBER, ROWS, RUNNING, SAVEPOINT, SCOPE, SCROLL, SEARCH,
SECOND, SEEK, SELECT, SENSITIVE, SESSION_USER, SET, SHOW, SKIP, SIMILAR, SMALLINT, SOME, SPECIFIC,
SPECIFICTYPE, SQL, SQLEXCEPTION, SQLSTATE, SQLWARNING, SQRT, START, STATIC, STDDEV, STDDEV_POP, STDDEV_SAMP,
STRING_AGG, SUBMULTISET, SUBSET, SUBSTRING, SUBSTRING_REGEX, SUCCEEDS, SUM, SYMMETRIC, SYSTEM, SYSTEM_TIME,
SYSTEM_USER, TABLE, TABLESAMPLE, THEN, TIME, TIMESTAMP, TIMEZONE_HOUR, TIMEZONE_MINUTE, TO, TRAILING,
TRANSLATE, TRANSLATE_REGEX, TRANSLATION, TREAT, TRIGGER, TRUNCATE, TRIM, TRIM_ARRAY, TRUE, UESCAPE, UNION,
UNIQUE, UNKNOWN, UNLOAD, UNNEST, UPDATE, UPPER, UPSERT, USER, USING, VALUE, VALUES, VALUE_OF, VAR, VAR_POP,
VAR_SAMP, VARBINARY, VARCHAR, VARYING, VERSIONING, WHEN, WHENEVER, WHERE, WIDTH_BUCKET, WINDOW, WITH,
WITHIN, WITHOUT, YEAR);
/**
* Hana SQLScript Constructor
*/
public HanaSQLScriptShallowParser() {
super(EHanaSQLScriptParserStates.class, EHanaSQLScriptParserStates.OUTSIDE_METHODS);
createMetaRules();
createMethodRules();
createStatementRules();
}
/**
* Create parser rules for generating meta shallow entities. Meta entities do not qualify as
* statements on their own.
*/
private void createMetaRules() {
// SET Statements ...
// ... SET HISTORY SESSION
inState(OUTSIDE_METHODS).sequence(SET, HISTORY, SESSION, TO).createNode(EShallowEntityType.META, "set", 1)
.skipTo(SEMICOLON).endNode();
// ... session variables
RecognizerBase otherSetStatementsMatch = inState(OUTSIDE_METHODS).sequence(SET,
HANA_SQLSCRIPT_IDENTIFIERS);
RecognizerBase sessionVarMatch = otherSetStatementsMatch
.repeated(HANA_SQLSCRIPT_IDENTIFIERS).sequence(EQUAL)
.createNode(EShallowEntityType.ATTRIBUTE, "session variable", 1);
sessionVarMatch.skipTo(SEMICOLON).endNode();
RecognizerBase schemaAndOtherMatch = otherSetStatementsMatch
.repeated(HANA_SQLSCRIPT_IDENTIFIERS).sequence(SEMICOLON).createNode(EShallowEntityType.META, "set", 1);
schemaAndOtherMatch.endNode();
createRulesForSQLStatements();
}
/**
* Create rules to match SQL statements that may be found in any state.
*/
private void createRulesForSQLStatements() {
// SQL
inAnyState().sequence(EnumSet.of(ALTER, COMMIT, DELETE, GRANT, LOCK, ROLLBACK, SAVEPOINT, SELECT, DROP, MERGE,
UPDATE, TRUNCATE, REVOKE, RENAME, LOAD, IMPORT, EXPORT, UPSERT, REPLACE, UNLOAD, BACKUP, RECOVER))
.createNode(EShallowEntityType.STATEMENT, "SQL", 0).skipTo(SEMICOLON).endNode();
// ... INSERT INTO statement
RecognizerBase insertMatch = inAnyState().sequence(INSERT, INTO)
.createNode(EShallowEntityType.STATEMENT, "SQL", 0);
RecognizerBase matchInsertTableName = matchTableNameForRule(insertMatch);
matchInsertTableName.skipTo(SEMICOLON).endNode();
// ... transactions
inAnyState().sequence(SET, TRANSACTION).createNode(EShallowEntityType.STATEMENT, "SQL", 1).skipTo(SEMICOLON)
.endNode();
// ... CREATE
createRuleForSQLCreate();
}
/**
* Creates rules for SQL CREATE statements.
*/
private void createRuleForSQLCreate() {
RecognizerBase createMatch = inAnyState().sequence(CREATE);
createMatch.sequence(EnumSet.of(FULLTEXT, GRAPH, SCHEMA, SEQUENCE, STATISTICS, VIEW))
.createNode(EShallowEntityType.STATEMENT, "SQL", 0).skipTo(SEMICOLON).endNode();
createMatch.optional(PUBLIC).sequence(SYNONYM).createNode(EShallowEntityType.STATEMENT, "SQL", 0)
.skipTo(SEMICOLON).endNode();
createMatch.optional(UNIQUE).optional(EnumSet.of(BTREE, CPBTREE)).sequence(INDEX)
.createNode(EShallowEntityType.STATEMENT, "SQL", 0).skipTo(SEMICOLON).endNode();
completeCreateTableRule(createMatch.optional(EnumSet.of(VIRTUAL, ROW, COLUMN)));
completeCreateTableRule(createMatch.optional(HISTORY, COLUMN));
completeCreateTableRule(createMatch.optional(EnumSet.of(LOCAL, GLOBAL), TEMPORARY).optional(COLUMN));
}
/**
* Completes the rule for matching SQL Create table statements for HANA SQLScript
*/
private static void completeCreateTableRule(RecognizerBase state) {
RecognizerBase matchTable = state.sequence(TABLE)
.createNode(EShallowEntityType.STATEMENT, "SQL", 0);
RecognizerBase matchTableName = matchTableNameForRule(matchTable);
matchTableName.skipTo(SEMICOLON).endNode();
}
/**
* For SQL Create and INSERT statements, matches the table name taking into account that temporary
* tables begin with a hash (#) symbol.
*/
private static RecognizerBase matchTableNameForRule(
RecognizerBase state) {
return state.optional(ETokenType.HASH).repeated(HANA_SQLSCRIPT_IDENTIFIERS, DOT)
.sequence(HANA_SQLSCRIPT_IDENTIFIERS);
}
/**
* Create parser rules for generating function and procedure shallow entities.
*/
private void createMethodRules() {
RecognizerBase methodStart = inState(OUTSIDE_METHODS).optional(CREATE).markStart()
.sequence(EnumSet.of(FUNCTION, PROCEDURE, TRIGGER)).repeated(HANA_SQLSCRIPT_IDENTIFIERS, DOT)
.sequence(HANA_SQLSCRIPT_IDENTIFIERS).createNode(EShallowEntityType.METHOD, 0, new Region(1, -1))
.skipNested(LPAREN, RPAREN);
RecognizerBase triggerMatch = methodStart.skipTo(ON).sequence(IDENTIFIER)
.skipBefore(BEGIN);
completeMethodMatch(triggerMatch);
RecognizerBase functionsAndProcsMatch = methodStart.skipBefore(AS).skipBefore(BEGIN)
.optional(SEQUENTIAL, EXECUTION);
completeMethodMatch(functionsAndProcsMatch);
}
/**
* Execute last rule in order to complete the match for method structures: triggers, functions and
* procedures.
*/
private static void completeMethodMatch(RecognizerBase match) {
match.sequence(BEGIN).parseUntil(METHOD_STATEMENTS).sequence(END).optional(SEMICOLON).endNode();
}
/**
* Create parser rules for generating shallow entities for simple and block statements inside a
* function or method.
*/
private void createStatementRules() {
createRulesForVariablesConstants();
createRulesForIfElse();
createRulesForCaseElse();
// BEGIN...END block
RecognizerBase blockHeadMatch = inState(METHOD_STATEMENTS).sequence(BEGIN)
.createNode(EShallowEntityType.STATEMENT, "block", 0);
completeAnonymousBlockMatch(blockHeadMatch.optional(AUTONOMOUS, TRANSACTION));
completeAnonymousBlockMatch(blockHeadMatch.optional(PARALLEL, EXECUTION));
// Loops
createRuleForLoops(WHILE, WHILE);
createRuleForLoops(FOR, FOR, IDENTIFIER, EnumSet.of(IN, AS));
// Assignment
Object assignmentOperators = EnumSet.of(EQUAL, ASSIGNMENT);
inState(METHOD_STATEMENTS).repeated(IDENTIFIER, DOT).sequence(IDENTIFIER).optional(LEFT_LABEL_BRACKET)
.skipBefore(assignmentOperators).sequence(assignmentOperators)
.createNode(EShallowEntityType.STATEMENT, "assignment", 0).skipTo(SEMICOLON).endNode();
// Internal procedure calls
inState(METHOD_STATEMENTS).sequence(CALL).repeated(HANA_SQLSCRIPT_IDENTIFIERS, DOT).markStart()
.sequence(HANA_SQLSCRIPT_IDENTIFIERS, LPAREN).skipTo(RPAREN)
.createNode(EShallowEntityType.STATEMENT, "procedure call", 0).endNode();
// Other statements
inState(METHOD_STATEMENTS)
.sequence(EnumSet.of(EXEC, SIGNAL, RESIGNAL, OPEN, CLOSE, BREAK, FETCH, CONTINUE, EXECUTE, RETURN))
.createNode(EShallowEntityType.STATEMENT, 0).skipTo(SEMICOLON).endNode();
}
/**
* Complete match for anonymous block
*/
private static void completeAnonymousBlockMatch(RecognizerBase state) {
state.parseUntil(METHOD_STATEMENTS).sequence(END).skipTo(SEMICOLON).endNode();
}
/**
* Creates rule for matching FOR and WHILE SQL loop statements.
*/
private void createRuleForLoops(Object endToken, Object... startTokens) {
inState(METHOD_STATEMENTS).sequence(startTokens).createNode(EShallowEntityType.STATEMENT, 0).skipTo(DO)
.parseUntil(METHOD_STATEMENTS).sequence(END, endToken, SEMICOLON).endNode();
}
/** Create rules for matching IF...THEN...ELSE...IF statements */
private void createRulesForIfElse() {
// IF...THEN...ELSEIF...ELSE
RecognizerBase ifAlternative = inState(METHOD_STATEMENTS)
.sequence(EnumSet.of(IF, ELSEIF)).createNode(EShallowEntityType.STATEMENT, 0)
.skipToWithNesting(THEN, CASE, END).parseUntil(METHOD_STATEMENTS)
.sequenceBefore(EnumSet.of(ELSEIF, ELSE, END));
ifAlternative.sequence(END, IF, SEMICOLON).endNode();
ifAlternative.endNodeWithContinuation();
// ELSE (both for if and case)
RecognizerBase elseMatcher = inState(METHOD_STATEMENTS).sequence(ELSE)
.createNode(EShallowEntityType.STATEMENT, 0).parseUntil(METHOD_STATEMENTS);
elseMatcher.sequence(END).skipTo(SEMICOLON).endNode();
elseMatcher.sequenceBefore(END, CASE).endNode();
}
/** Create rules for matching CASE...WHEN...THEN...ELSE statements */
private void createRulesForCaseElse() {
// CASE
inState(METHOD_STATEMENTS).sequence(CASE, IDENTIFIER).createNode(EShallowEntityType.STATEMENT, 0)
.parseUntil(METHOD_STATEMENTS).endNode();
// WHEN in case
inState(METHOD_STATEMENTS).sequence(WHEN).skipTo(THEN).createNode(EShallowEntityType.STATEMENT, "when", 1)
.endNode();
}
/** Creates rules for matching variables, constants, cursors, e.t.c. */
private void createRulesForVariablesConstants() {
RecognizerBase declareMatch = inState(METHOD_STATEMENTS).sequence(DECLARE);
// Cursors.
declareMatch.sequence(CURSOR).skipTo(FOR).createNode(EShallowEntityType.ATTRIBUTE, "cursor", 2).sequence(SELECT)
.skipTo(SEMICOLON).endNode();
// (exit) Exception handler, with an accompanying select statement or
// method block
RecognizerBase exitHandlerMatch = declareMatch.sequence(EXIT, HANDLER, FOR);
RecognizerBase exitHandlerForSQLExceptionAndConditionMatch = exitHandlerMatch
.sequence(EnumSet.of(SQLEXCEPTION, IDENTIFIER));
RecognizerBase exitHandlerForSQLErrorMatch = exitHandlerMatch
.sequence(SQL_ERROR_CODE, INTEGER_LITERAL);
completeRuleForExitHandlerBlocks(exitHandlerForSQLExceptionAndConditionMatch, -2);
completeRuleForExitHandlerBlocks(exitHandlerForSQLErrorMatch, -3);
exitHandlerForSQLExceptionAndConditionMatch.createNode(EShallowEntityType.ATTRIBUTE, "exit handler", -1)
.optional(SELECT).skipTo(SEMICOLON).endNode();
exitHandlerForSQLErrorMatch.createNode(EShallowEntityType.ATTRIBUTE, "exit handler", -2).optional(SELECT)
.skipTo(SEMICOLON).endNode();
// Conditions
declareMatch.sequence(IDENTIFIER, CONDITION).optional(FOR, IDENTIFIER)
.createNode(EShallowEntityType.ATTRIBUTE, "condition variable", 1).endNode();
// ... simple, constant and condition variable declarations
declareMatch.sequence(IDENTIFIER).repeated(HANA_SQLSCRIPT_IDENTIFIERS)
.createNode(EShallowEntityType.ATTRIBUTE, "variable", 1).skipTo(SEMICOLON).endNode();
}
/**
* Conclude rule for matching exit handler blocks
*/
private static void completeRuleForExitHandlerBlocks(RecognizerBase state, int pos) {
state.sequence(BEGIN).createNode(EShallowEntityType.STATEMENT, "exit handler block", pos)
.parseUntil(METHOD_STATEMENTS).sequence(END, SEMICOLON).endNode();
}
}