diff --git a/docs/pages/release_notes.md b/docs/pages/release_notes.md index fb34d19bc6..9d840ada14 100644 --- a/docs/pages/release_notes.md +++ b/docs/pages/release_notes.md @@ -58,6 +58,7 @@ the implementation based on your feedback. * [#2141](https://github.com/pmd/pmd/issues/2141): \[java] StringInstatiation: False negative with String-array access * plsql * [#2008](https://github.com/pmd/pmd/issues/2008): \[plsql] In StringLiteral using alternative quoting mechanism single quotes cause parsing errors + * [#2009](https://github.com/pmd/pmd/issues/2009): \[plsql] Multiple DDL commands are skipped during parsing ### API Changes diff --git a/pmd-plsql/etc/grammar/PldocAST.jjt b/pmd-plsql/etc/grammar/PldocAST.jjt index 27790459c8..da37ca0f3d 100644 --- a/pmd-plsql/etc/grammar/PldocAST.jjt +++ b/pmd-plsql/etc/grammar/PldocAST.jjt @@ -227,6 +227,9 @@ public class PLSQLParser { /** * Semantic lookahead to check if the next identifier is a * specific keyword. + * + *

+ * Usage: LOOKAHEAD({isKeyword("WAIT")}) KEYWORD("WAIT") */ private boolean isKeyword(String keyword) { return getToken(1).kind == IDENTIFIER && getToken(1).image.equalsIgnoreCase(keyword); @@ -259,13 +262,13 @@ ASTInput Input(String sourcecode) : {} | LOOKAHEAD(6) Directory() | LOOKAHEAD(6) DatabaseLink() | LOOKAHEAD(6) Global() - | LOOKAHEAD(6) DDLCommand() //Ignore any other DDL Event + | (LOOKAHEAD(6) DDLCommand())+ | LOOKAHEAD(2) SqlPlusCommand() | UpdateStatement() | DeleteStatement() | InsertStatement() - | SelectStatement() (";")? - |(|||||) SkipPastNextTokenOccurrence(SQLPLUS_TERMINATOR) //Ignore SQL statements in scripts + | SelectStatement() [";"] + |(|||
||) ReadPastNextOccurrence(";") //Ignore SQL statements in scripts ) ("/")* )* @@ -279,8 +282,15 @@ ASTDDLCommand DDLCommand() : } { ( - simpleNode = DDLEvent() - SkipPastNextTokenOccurrence(SQLPLUS_TERMINATOR) + ( + LOOKAHEAD({isKeyword("COMMENT")}) + simpleNode = Comment() + ) + | + ( + simpleNode = DDLEvent() + ReadPastNextOccurrence(";") + ) ) { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; } } @@ -315,7 +325,7 @@ ASTSqlPlusCommand SqlPlusCommand() : | | // DDL that might be encountered - | + | LOOKAHEAD({isKeyword("COMMENT")}) KEYWORD("COMMENT") | | | @@ -1121,6 +1131,7 @@ ASTRead2NextOccurrence Read2NextOccurrence(String target) : { nextToken = getNextToken(); sb.append(nextToken.image); + sb.append(' '); nextToken = getToken(1); } } @@ -1133,10 +1144,11 @@ ASTRead2NextOccurrence Read2NextOccurrence(String target) : */ ASTReadPastNextOccurrence ReadPastNextOccurrence(String target) : { + ASTRead2NextOccurrence skipped = Read2NextOccurrence(target); + StringBuilder sb = new StringBuilder(); - Token t = null; - sb.append(Read2NextOccurrence(target)) ; - t = getNextToken(); // Chomp this one + sb.append(skipped.getImage()) ; + Token t = getNextToken(); // Chomp this one sb.append(t.image); } { @@ -3860,19 +3872,22 @@ ASTViewColumn ViewColumn() : { return jjtThis ; } } +/** + * https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/COMMENT.html#GUID-65F447C4-6914-4823-9691-F15D52DB74D7 + */ ASTComment Comment() : { } { - ( + LOOKAHEAD({isKeyword("COMMENT")}) KEYWORD("COMMENT") { jjtThis.setImage(token.image); } + ( ((
| | ) [LOOKAHEAD(2) ID()"."] ID()) | ( [LOOKAHEAD(ID()"."ID()"."ID()) ID()"."] ID() "." ID()) ) - - [";"] - { return jjtThis ; } + StringLiteral() + ";" + { return jjtThis ; } } -// SRT * / @@ -4445,23 +4460,26 @@ ASTNonDMLTrigger NonDMLTrigger() : { return jjtThis ; } } - +/** + * https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/Types-of-SQL-Statements.html#GUID-FD9A8CB4-6B9A-44E5-B114-EFB8DA76FC88 + */ ASTDDLEvent DDLEvent(): {} { ( | | | - | + | LOOKAHEAD({isKeyword("COMMENT")}) KEYWORD("COMMENT") | | | + // | | | + // | | | | - | ) { jjtThis.setImage(token.toString()) ; jjtThis.value = token ; return jjtThis ; } } @@ -4697,7 +4715,6 @@ TOKEN [IGNORE_CASE]: | | | - | | | | @@ -4953,7 +4970,6 @@ TOKEN [IGNORE_CASE]: | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax -| //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax | //11G Trigger Syntax @@ -5082,8 +5098,6 @@ TOKEN : < JAVA_INTERFACE_CLASS: ( "SQLData" | "CustomDatum" | "OraData" ) > //| //< #BOOLEAN_LITERAL: "TRUE" | "FALSE" > -| - } /** @@ -5360,7 +5374,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| //| -| | //| //| @@ -5421,7 +5434,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {} //| //| | -| //| //| | @@ -6326,7 +6338,7 @@ ASTID ID(): {} | KEYWORD_UNRESERVED() //SRT 2011-04-17 /*KEYWORDS_UNRESERVED | | | | | | | | | - | | | | + | | | */ //20120501 | | | @@ -6589,7 +6601,6 @@ ASTQualifiedID QualifiedID(): {} // //| //20120501 | - //| //| // // diff --git a/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/MultipleDDLStatementsTest.java b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/MultipleDDLStatementsTest.java new file mode 100644 index 0000000000..cbd5a74497 --- /dev/null +++ b/pmd-plsql/src/test/java/net/sourceforge/pmd/lang/plsql/ast/MultipleDDLStatementsTest.java @@ -0,0 +1,29 @@ +/* + * BSD-style license; for more info see http://pmd.sourceforge.net/license.html + */ + +package net.sourceforge.pmd.lang.plsql.ast; + +import java.nio.charset.StandardCharsets; +import java.util.List; + +import org.apache.commons.io.IOUtils; +import org.junit.Assert; +import org.junit.Test; + +import net.sourceforge.pmd.lang.plsql.AbstractPLSQLParserTst; + +public class MultipleDDLStatementsTest extends AbstractPLSQLParserTst { + + @Test + public void parseDDLCommands() throws Exception { + String code = IOUtils.toString(this.getClass().getResourceAsStream("DDLCommands.sql"), + StandardCharsets.UTF_8); + ASTInput input = parsePLSQL(code); + List ddlcommands = input.findDescendantsOfType(ASTDDLCommand.class); + Assert.assertEquals(4, ddlcommands.size()); + List comments = input.findDescendantsOfType(ASTComment.class); + Assert.assertEquals(3, comments.size()); + Assert.assertEquals("'abbreviated job title'", comments.get(0).getFirstChildOfType(ASTStringLiteral.class).getImage()); + } +} diff --git a/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/DDLCommands.sql b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/DDLCommands.sql new file mode 100644 index 0000000000..5833689339 --- /dev/null +++ b/pmd-plsql/src/test/resources/net/sourceforge/pmd/lang/plsql/ast/DDLCommands.sql @@ -0,0 +1,7 @@ +COMMENT ON COLUMN employees.job_id IS 'abbreviated job title'; + +COMMENT ON COLUMN employees.job_id IS 'abbreviated job title'; + +DROP TABLE employees; + +COMMENT ON COLUMN employees.job_id IS 'abbreviated job title';