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';