Merge remote-tracking branch 'adangel/issue-1587' into plsql-parser-fixes2

This commit is contained in:
Andreas Dangel
2019-02-16 20:00:39 +01:00
10 changed files with 279 additions and 3 deletions

View File

@ -37,6 +37,8 @@ This is a {{ site.pmd.release_type }} release.
* [#1633](https://github.com/pmd/pmd/issues/1633): \[java] UnsynchronizedStaticFormatter reports commons lang FastDateFormat
* java-performance
* [#1632](https://github.com/pmd/pmd/issues/1632): \[java] ConsecutiveLiteralAppends false positive over catch
* plsql
* [#1587](https://github.com/pmd/pmd/issues/1587): \[plsql] Parse Exception with EXISTS
### API Changes

View File

@ -1300,6 +1300,8 @@ void Condition2() #void :
(
// a IS OF Type condition that starts with a function...
LOOKAHEAD(FunctionCall() <IS> <OF>) IsOfTypeCondition()
|
LOOKAHEAD(9) MultisetCondition()
|
LOOKAHEAD(9) ComparisonCondition()
|
@ -1308,12 +1310,16 @@ void Condition2() #void :
LOOKAHEAD(9) InCondition()
|
LOOKAHEAD(9) LikeCondition()
|
LOOKAHEAD(9) RegexpLikeCondition()
|
LOOKAHEAD(4) BetweenCondition()
|
LOOKAHEAD(4) IsNullCondition()
|
LOOKAHEAD(4) IsOfTypeCondition()
|
ExistsCondition()
)
}
@ -1338,6 +1344,67 @@ ASTLikeCondition LikeCondition() :
{ return jjtThis; }
}
ASTRegexpLikeCondition RegexpLikeCondition() :
{
Token matchParam = null;
}
{
<REGEXP_LIKE> "("
ID() ","
StringLiteral()
[ "," ( matchParam = <CHARACTER_LITERAL> | matchParam = <STRING_LITERAL> ) {jjtThis.setMatchParam(matchParam.toString());} ]
")"
{ return jjtThis; }
}
ASTExistsCondition ExistsCondition() :
{}
{
<EXISTS> "(" Subquery() ")"
{ return jjtThis; }
}
void MultisetCondition() #void :
{}
{
LOOKAHEAD(4) IsASetCondition()
|
LOOKAHEAD(4) IsEmptyCondition()
|
LOOKAHEAD(4) MemberCondition()
|
LOOKAHEAD(4) SubmultisetCondition()
}
ASTIsASetCondition IsASetCondition() :
{}
{
TableName() <IS> [<NOT>] <A> <SET>
{ return jjtThis; }
}
ASTIsEmptyCondition IsEmptyCondition() :
{}
{
TableName() <IS> [<NOT>] <EMPTY>
{ return jjtThis; }
}
ASTMemberCondition MemberCondition() :
{}
{
SqlExpression() [<NOT>] <MEMBER> [<OF>] TableName()
{ return jjtThis; }
}
ASTSubmultisetCondition SubmultisetCondition() :
{}
{
TableName() [<NOT>] <SUBMULTISET> [<OF>] TableName()
{ return jjtThis; }
}
ASTCompoundCondition CompoundCondition() :
{}
{
@ -4313,6 +4380,7 @@ TOKEN [IGNORE_CASE]:
<RECORD: "RECORD"> |
<REF: "REF"> |
<REFERENCES: "REFERENCES"> |
<REGEXP_LIKE: "REGEXP_LIKE"> |
<RELEASE: "RELEASE"> |
<RELIES_ON: "RELIES_ON"> |
<RENAME: "RENAME"> | //SRT 2011-04-17
@ -5471,7 +5539,7 @@ ASTKEYWORD_UNRESERVED KEYWORD_UNRESERVED (): {}
| <REFERENCES>
//| <REFERENCING>
//| <REFRESH>
//| <REGEXP_LIKE>
| <REGEXP_LIKE>
//| <REGISTER>
//| <REJECT>
//| <REKEY>
@ -5848,7 +5916,7 @@ ASTID ID(): {}
| <EXCEPTION> //SYNTAX
| <EXCLUSIVE> //SYNTAX //RESERVED WORD
| <EXECUTE> //SYNTAX
| <EXISTS> //SYNTAX //RESERVED WORD
//| <EXISTS> //SYNTAX //RESERVED WORD
//| <EXIT> //SYNTAX
//20120501 | <EXTENDS>
| <FETCH> //SYNTAX
@ -5894,7 +5962,7 @@ ASTID ID(): {}
| <NUMBER> //RESERVED WORD
| <NUMBER_BASE>
| <OCIROWID>
| <OF> //RESERVED WORD
//| <OF> //RESERVED WORD
//| <ON> //RESERVED WORD
//20120501 | <OPAQUE>
| <BFILE_BASE>

View File

@ -62,6 +62,7 @@
<delete file="${target}/net/sourceforge/pmd/lang/plsql/ast/ASTPrimaryPrefix.java" />
<delete file="${target}/net/sourceforge/pmd/lang/plsql/ast/ASTPrimarySuffix.java" />
<delete file="${target}/net/sourceforge/pmd/lang/plsql/ast/ASTProgramUnit.java" />
<delete file="${target}/net/sourceforge/pmd/lang/plsql/ast/ASTRegexpLikeCondition.java" />
<delete file="${target}/net/sourceforge/pmd/lang/plsql/ast/ASTSelectIntoStatement.java" />
<delete file="${target}/net/sourceforge/pmd/lang/plsql/ast/ASTSelectStatement.java" />
<delete file="${target}/net/sourceforge/pmd/lang/plsql/ast/ASTSubqueryOperation.java" />

View File

@ -0,0 +1,40 @@
/**
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.plsql.ast;
/* JavaCCOptions:MULTI=true,NODE_USES_PARSER=true,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=AST,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */
public class ASTRegexpLikeCondition extends net.sourceforge.pmd.lang.plsql.ast.AbstractPLSQLNode {
private String matchParam;
public ASTRegexpLikeCondition(int id) {
super(id);
}
public ASTRegexpLikeCondition(PLSQLParser p, int id) {
super(p, id);
}
@Override
public Object jjtAccept(PLSQLParserVisitor visitor, Object data) {
return visitor.visit(this, data);
}
void setMatchParam(String matchParam) {
this.matchParam = matchParam;
}
public String getMatchParam() {
return this.matchParam;
}
public ASTID getSourceChar() {
return (ASTID) jjtGetChild(0);
}
public ASTStringLiteral getPattern() {
return (ASTStringLiteral) jjtGetChild(1);
}
}
/* JavaCC - OriginalChecksum=afb8806a0c67f95b736d6e8bc46def15 (do not edit this line) */

View File

@ -986,4 +986,34 @@ public class PLSQLParserVisitorAdapter implements PLSQLParserVisitor {
public Object visit(ASTValuesClause node, Object data) {
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTExistsCondition node, Object data) {
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTIsASetCondition node, Object data) {
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTIsEmptyCondition node, Object data) {
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTMemberCondition node, Object data) {
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTSubmultisetCondition node, Object data) {
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTRegexpLikeCondition node, Object data) {
return visit((PLSQLNode) node, data);
}
}

View File

@ -1080,6 +1080,36 @@ public abstract class AbstractPLSQLRule extends AbstractRule implements PLSQLPar
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTExistsCondition node, Object data) {
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTIsASetCondition node, Object data) {
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTIsEmptyCondition node, Object data) {
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTMemberCondition node, Object data) {
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTSubmultisetCondition node, Object data) {
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTRegexpLikeCondition node, Object data) {
return visit((PLSQLNode) node, data);
}
/*
* Treat all Executable Code
*/

View File

@ -68,4 +68,30 @@ public class WhereClauseTest extends AbstractPLSQLParserTst {
StandardCharsets.UTF_8);
ASTInput input = parsePLSQL(code);
}
@Test
public void testExistsCondition() throws Exception {
String code = IOUtils.toString(this.getClass().getResourceAsStream("WhereClauseExists.pls"),
StandardCharsets.UTF_8);
ASTInput input = parsePLSQL(code);
}
@Test
public void testMultisetCondition() throws Exception {
String code = IOUtils.toString(this.getClass().getResourceAsStream("WhereClauseMultiset.pls"),
StandardCharsets.UTF_8);
ASTInput input = parsePLSQL(code);
}
@Test
public void testRegexpLikeCondition() throws Exception {
String code = IOUtils.toString(this.getClass().getResourceAsStream("WhereClauseRegexpLike.pls"),
StandardCharsets.UTF_8);
ASTInput input = parsePLSQL(code);
List<ASTRegexpLikeCondition> regexps = input.findDescendantsOfType(ASTRegexpLikeCondition.class);
Assert.assertEquals(2, regexps.size());
Assert.assertEquals("last_name", regexps.get(1).getSourceChar().getImage());
Assert.assertEquals("'([aeiou])\\1'", regexps.get(1).getPattern().getImage());
Assert.assertEquals("'i'", regexps.get(1).getMatchParam());
}
}

View File

@ -0,0 +1,24 @@
--
-- Where Clause With Exists Condition
-- https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/EXISTS-Condition.html#GUID-20259A83-C42B-4E0D-8DF4-9A2A66ACA8E7
--
BEGIN
SELECT id
INTO id_out
FROM some_table
WHERE EXISTS
(SELECT NULL
FROM other_table
WHERE other_id = other_id_in);
DELETE FROM some_table
WHERE id = id_in
AND NOT EXISTS
(SELECT NULL
FROM other_table
WHERE id = id_in);
END;
/

View File

@ -0,0 +1,35 @@
--
-- Where Clause With Multiset Conditions
-- https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/Multiset-Conditions.html#GUID-E8164A15-715A-40A0-944D-26DF4C84DE3F
--
BEGIN
SELECT customer_id, cust_address_ntab
INTO test
FROM customers_demo
WHERE cust_address_ntab IS A SET
ORDER BY customer_id;
SELECT product_id, TO_CHAR(ad_finaltext) AS text
INTO test
FROM print_media
WHERE ad_textdocs_ntab IS NOT EMPTY
ORDER BY product_id, text;
SELECT customer_id, cust_address_ntab
INTO test
FROM customers_demo
WHERE cust_address_typ('8768 N State Rd 37', 47404,
'Bloomington', 'IN', 'US')
MEMBER OF cust_address_ntab
ORDER BY customer_id;
SELECT customer_id, cust_address_ntab
INTO test
FROM customers_demo
WHERE cust_address_ntab SUBMULTISET OF cust_address2_ntab
ORDER BY customer_id;
END;
/

View File

@ -0,0 +1,20 @@
--
-- Where Clause With Regexp Like
-- https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/Pattern-matching-Conditions.html#GUID-D2124F3A-C6E4-4CCA-A40E-2FFCABFD8E19
--
BEGIN
SELECT first_name, last_name
INTO test
FROM employees
WHERE REGEXP_LIKE (first_name, '^Ste(v|ph)en$')
ORDER BY first_name, last_name;
SELECT last_name
INTO test
FROM employees
WHERE REGEXP_LIKE (last_name, '([aeiou])\1', 'i')
ORDER BY last_name;
END;
/