Merge branch 'pr-2184'

[plsql] Fix DDLCommands parsing
This commit is contained in:
Andreas Dangel
2020-01-05 19:20:29 +01:00
4 changed files with 73 additions and 25 deletions

View File

@ -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 * [#2141](https://github.com/pmd/pmd/issues/2141): \[java] StringInstatiation: False negative with String-array access
* plsql * plsql
* [#2008](https://github.com/pmd/pmd/issues/2008): \[plsql] In StringLiteral using alternative quoting mechanism single quotes cause parsing errors * [#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 ### API Changes

View File

@ -227,6 +227,9 @@ public class PLSQLParser {
/** /**
* Semantic lookahead to check if the next identifier is a * Semantic lookahead to check if the next identifier is a
* specific keyword. * specific keyword.
*
* <p>
* Usage: <code>LOOKAHEAD({isKeyword("WAIT")}) KEYWORD("WAIT")</code>
*/ */
private boolean isKeyword(String keyword) { private boolean isKeyword(String keyword) {
return getToken(1).kind == IDENTIFIER && getToken(1).image.equalsIgnoreCase(keyword); return getToken(1).kind == IDENTIFIER && getToken(1).image.equalsIgnoreCase(keyword);
@ -259,13 +262,13 @@ ASTInput Input(String sourcecode) : {}
| LOOKAHEAD(6) Directory() | LOOKAHEAD(6) Directory()
| LOOKAHEAD(6) DatabaseLink() | LOOKAHEAD(6) DatabaseLink()
| LOOKAHEAD(6) Global() | LOOKAHEAD(6) Global()
| LOOKAHEAD(6) DDLCommand() //Ignore any other DDL Event | (LOOKAHEAD(6) DDLCommand())+
| LOOKAHEAD(2) SqlPlusCommand() | LOOKAHEAD(2) SqlPlusCommand()
| UpdateStatement() | UpdateStatement()
| DeleteStatement() | DeleteStatement()
| InsertStatement() | InsertStatement()
| SelectStatement() (";")? | SelectStatement() [";"]
|(<COMMIT>|<ROLLBACK>|<SAVEPOINT>|<LOCK><TABLE>|<MERGE>|<WITH>) SkipPastNextTokenOccurrence(SQLPLUS_TERMINATOR) //Ignore SQL statements in scripts |(<COMMIT>|<ROLLBACK>|<SAVEPOINT>|<LOCK><TABLE>|<MERGE>|<WITH>) 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 ; } { jjtThis.setImage(simpleNode.getImage()) ; return jjtThis ; }
} }
@ -315,7 +325,7 @@ ASTSqlPlusCommand SqlPlusCommand() :
| <VARIABLE> | <VARIABLE>
| <WHENEVER> | <WHENEVER>
// DDL that might be encountered // DDL that might be encountered
| <COMMENT> | LOOKAHEAD({isKeyword("COMMENT")}) KEYWORD("COMMENT")
| <GRANT> | <GRANT>
| <REVOKE> | <REVOKE>
| <DROP> | <DROP>
@ -1121,6 +1131,7 @@ ASTRead2NextOccurrence Read2NextOccurrence(String target) :
{ {
nextToken = getNextToken(); nextToken = getNextToken();
sb.append(nextToken.image); sb.append(nextToken.image);
sb.append(' ');
nextToken = getToken(1); nextToken = getToken(1);
} }
} }
@ -1133,10 +1144,11 @@ ASTRead2NextOccurrence Read2NextOccurrence(String target) :
*/ */
ASTReadPastNextOccurrence ReadPastNextOccurrence(String target) : ASTReadPastNextOccurrence ReadPastNextOccurrence(String target) :
{ {
ASTRead2NextOccurrence skipped = Read2NextOccurrence(target);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
Token t = null; sb.append(skipped.getImage()) ;
sb.append(Read2NextOccurrence(target)) ; Token t = getNextToken(); // Chomp this one
t = getNextToken(); // Chomp this one
sb.append(t.image); sb.append(t.image);
} }
{ {
@ -3860,19 +3872,22 @@ ASTViewColumn ViewColumn() :
{ return jjtThis ; } { return jjtThis ; }
} }
/**
* https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/COMMENT.html#GUID-65F447C4-6914-4823-9691-F15D52DB74D7
*/
ASTComment Comment() : ASTComment Comment() :
{ {
} }
{ {
<COMMENT> <ON> ( LOOKAHEAD({isKeyword("COMMENT")}) KEYWORD("COMMENT") { jjtThis.setImage(token.image); }
<ON> (
((<TABLE> | <OPERATOR> | <INDEXTYPE>) [LOOKAHEAD(2) ID()"."] ID()) | ((<TABLE> | <OPERATOR> | <INDEXTYPE>) [LOOKAHEAD(2) ID()"."] ID()) |
(<COLUMN> [LOOKAHEAD(ID()"."ID()"."ID()) ID()"."] ID() "." ID()) (<COLUMN> [LOOKAHEAD(ID()"."ID()"."ID()) ID()"."] ID() "." ID())
) )
<IS> <STRING_LITERAL> <IS> StringLiteral()
[";"] ";"
{ return jjtThis ; } { return jjtThis ; }
} }
// SRT * /
@ -4445,23 +4460,26 @@ ASTNonDMLTrigger NonDMLTrigger() :
{ return jjtThis ; } { 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(): {} ASTDDLEvent DDLEvent(): {}
{ {
( <ALTER> ( <ALTER>
| <ANALYZE> | <ANALYZE>
| <ASSOCIATE> <STATISTICS> | <ASSOCIATE> <STATISTICS>
| <AUDIT> | <AUDIT>
| <COMMENT> | LOOKAHEAD({isKeyword("COMMENT")}) KEYWORD("COMMENT")
| <CREATE> | <CREATE>
| <DISASSOCIATE> <STATISTICS> | <DISASSOCIATE> <STATISTICS>
| <DROP> | <DROP>
// | <FLASHBACK>
| <GRANT> | <GRANT>
| <NOAUDIT> | <NOAUDIT>
// | <PURGE>
| <RENAME> | <RENAME>
| <REVOKE> | <REVOKE>
| <TRUNCATE> | <TRUNCATE>
| <DDL>
) )
{ jjtThis.setImage(token.toString()) ; jjtThis.value = token ; return jjtThis ; } { jjtThis.setImage(token.toString()) ; jjtThis.value = token ; return jjtThis ; }
} }
@ -4697,7 +4715,6 @@ TOKEN [IGNORE_CASE]:
<COALESCE: "COALESCE"> | <COALESCE: "COALESCE"> |
<COLLECT: "COLLECT"> | <COLLECT: "COLLECT"> |
<COLUMN: "COLUMN"> | <COLUMN: "COLUMN"> |
<COMMENT: "COMMENT"> |
<COMMIT: "COMMIT"> | <COMMIT: "COMMIT"> |
<CONSTANT: "CONSTANT"> | <CONSTANT: "CONSTANT"> |
<CONSTRAINT: "CONSTRAINT"> | <CONSTRAINT: "CONSTRAINT"> |
@ -4953,7 +4970,6 @@ TOKEN [IGNORE_CASE]:
| <COMPOUND: "COMPOUND"> //11G Trigger Syntax | <COMPOUND: "COMPOUND"> //11G Trigger Syntax
| <DATABASE: "DATABASE"> //11G Trigger Syntax | <DATABASE: "DATABASE"> //11G Trigger Syntax
| <CALL: "CALL"> //11G Trigger Syntax | <CALL: "CALL"> //11G Trigger Syntax
| <DDL: "DDL"> //11G Trigger Syntax
| <DISASSOCIATE: "DISASSOCIATE"> //11G Trigger Syntax | <DISASSOCIATE: "DISASSOCIATE"> //11G Trigger Syntax
| <EACH: "EACH"> //11G Trigger Syntax | <EACH: "EACH"> //11G Trigger Syntax
| <FOLLOWS: "FOLLOWS"> //11G Trigger Syntax | <FOLLOWS: "FOLLOWS"> //11G Trigger Syntax
@ -5082,8 +5098,6 @@ TOKEN :
< JAVA_INTERFACE_CLASS: ( "SQLData" | "CustomDatum" | "OraData" ) > < JAVA_INTERFACE_CLASS: ( "SQLData" | "CustomDatum" | "OraData" ) >
//| //|
//< #BOOLEAN_LITERAL: "TRUE" | "FALSE" > //< #BOOLEAN_LITERAL: "TRUE" | "FALSE" >
|
<SQLPLUS_TERMINATOR: ( ";" | "/" ) >
} }
/** /**
@ -5360,7 +5374,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {}
//| <COLUMN_STATS> //| <COLUMN_STATS>
//| <COLUMN_VALUE> //| <COLUMN_VALUE>
//| <COLUMNS> //| <COLUMNS>
| <COMMENT>
| <COMMIT> | <COMMIT>
//| <COMMITTED> //| <COMMITTED>
//| <COMPACT> //| <COMPACT>
@ -5421,7 +5434,6 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {}
//| <DBA_RECYCLEBIN> //| <DBA_RECYCLEBIN>
//| <DBMS_STATS> //| <DBMS_STATS>
| <DBTIMEZONE> | <DBTIMEZONE>
| <DDL>
//| <DEALLOCATE> //| <DEALLOCATE>
//| <DEBUG> //| <DEBUG>
| <DEC> | <DEC>
@ -6326,7 +6338,7 @@ ASTID ID(): {}
| KEYWORD_UNRESERVED() //SRT 2011-04-17 | KEYWORD_UNRESERVED() //SRT 2011-04-17
/*KEYWORDS_UNRESERVED /*KEYWORDS_UNRESERVED
|<EXTRACT> | <FALSE> | <TRUE> | <SECOND> | <MINUTE> | <HOUR> | <DAY> | <MONTH> | <YEAR> |<EXTRACT> | <FALSE> | <TRUE> | <SECOND> | <MINUTE> | <HOUR> | <DAY> | <MONTH> | <YEAR>
| <NO> |<ROW> | <COMMENT> | <CURSOR> | <NO> |<ROW> | <CURSOR>
*/ */
//20120501 | <DEFINER> //20120501 | <DEFINER>
| <SERIALLY_REUSABLE> | <RESTRICT_REFERENCES> | <SERIALLY_REUSABLE> | <RESTRICT_REFERENCES>
@ -6589,7 +6601,6 @@ ASTQualifiedID QualifiedID(): {}
//<CLUSTER> //<CLUSTER>
//| <COALESCE> //| <COALESCE>
//20120501 | <COLLECT> //20120501 | <COLLECT>
//| <COMMENT>
//| <COMMIT> //| <COMMIT>
//<COMPRESS> //<COMPRESS>
//<CONNECT> //<CONNECT>

View File

@ -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<ASTDDLCommand> ddlcommands = input.findDescendantsOfType(ASTDDLCommand.class);
Assert.assertEquals(4, ddlcommands.size());
List<ASTComment> comments = input.findDescendantsOfType(ASTComment.class);
Assert.assertEquals(3, comments.size());
Assert.assertEquals("'abbreviated job title'", comments.get(0).getFirstChildOfType(ASTStringLiteral.class).getImage());
}
}

View File

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