All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.cqse.check.framework.shallowparser.languages.hanasqlscript.HanaSQLScriptShallowParser Maven / Gradle / Ivy

Go to download

The Teamscale Custom Check API allows users to extend Teamscale by writing custom analyses that create findings.

There is a newer version: 2024.7.2
Show newest version
/*
 * 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();
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy