forked from phoedos/pmd
Merge remote-tracking branch 'adangel/issue-1587' into plsql-parser-fixes2
This commit is contained in:
@ -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
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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" />
|
||||
|
@ -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) */
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
/
|
@ -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;
|
||||
/
|
@ -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;
|
||||
/
|
Reference in New Issue
Block a user