[plsql] Add SimpleExpressionNode, allow expressions with REGEXP_LIKE

This commit is contained in:
Andreas Dangel committed 2019-02-16 20:25:50 +01:00
1 parent 984928ad52
commit 808c03f532
8 files changed
+104 -29

No files matched your search

+50 -24
View File
@@ -1305,6 +1305,8 @@ void Condition2() #void :
(
// a IS OF Type condition that starts with a function...
LOOKAHEAD(FunctionCall() <IS> [<NOT>] <OF>) IsOfTypeCondition()
|
LOOKAHEAD(<REGEXP_LIKE>) RegexpLikeCondition()
|
LOOKAHEAD(9) MultisetCondition()
|
@@ -1315,8 +1317,6 @@ void Condition2() #void :
LOOKAHEAD(9) InCondition()
|
LOOKAHEAD(9) LikeCondition()
|
LOOKAHEAD(9) RegexpLikeCondition()
|
LOOKAHEAD(4) BetweenCondition()
|
@@ -1355,8 +1355,8 @@ ASTRegexpLikeCondition RegexpLikeCondition() :
}
{
<REGEXP_LIKE> "("
ID() ","
StringLiteral()
SqlExpression() ","
SqlExpression()
[ "," ( matchParam = <CHARACTER_LITERAL> | matchParam = <STRING_LITERAL> ) {jjtThis.setMatchParam(matchParam.toString());} ]
")"
{ return jjtThis; }
@@ -1473,26 +1473,51 @@ ASTExpressionList ExpressionList() :
* @see https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/About-SQL-Expressions.html
*/
ASTSqlExpression SqlExpression() :
{}
{ PLSQLNode simpleNode = null; }
{
[ LOOKAHEAD(2) <PRIOR> ]
// SimpleExpression
(
LOOKAHEAD(3) AdditiveExpression() // this can be a literal or a simple expression, but no conditional
|
LOOKAHEAD(5) SchemaName() "." TableName() "." Column()
|
LOOKAHEAD(3) TableName() "." Column()
|
LOOKAHEAD(2) Column()
|
LOOKAHEAD(2) <ROWNUM>
|
LOOKAHEAD(2) "(" SelectStatement() ")" // see "Scalar Subquery Expressions"
// AdditiveExpression includes literals and simple expressions, but no conditional
( simpleNode = AdditiveExpression() ) { jjtThis.setImage(simpleNode.getImage()); }
)
{ return jjtThis; }
}
/**
* https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/Simple-Expressions.html
*/
ASTSimpleExpression SimpleExpression() :
{ StringBuilder sb = new StringBuilder(); }
{
(
LOOKAHEAD(2) <ROWNUM> { sb.append(token.image); }
|
StringLiteral() { sb.append(token.image); }
|
NumericLiteral() { sb.append(token.image); }
|
LOOKAHEAD(2) NullLiteral() { sb.append(token.image); }
|
// sequence
LOOKAHEAD(4) ID() "." ( <CURRVAL> | <NEXTVAL> )
|
LOOKAHEAD(6)
SchemaName() { sb.append(token.image); } "." { sb.append(token.image); }
TableName() { sb.append(token.image); } "." { sb.append(token.image); }
Column() { sb.append(token.image); }
|
LOOKAHEAD(4)
TableName() { sb.append(token.image); } "." { sb.append(token.image); }
Column() { sb.append(token.image); }
|
LOOKAHEAD(2)
Column() { sb.append(token.image); }
)
{
jjtThis.setImage(sb.toString());
return jjtThis;
}
}
/**
* Built-in function call or a user defined function call.
* See https://docs.oracle.com/en/database/oracle/oracle-database/18/sqlrf/Functions.html#GUID-D079EFD3-C683-441F-977E-2C9503089982
@@ -2938,6 +2963,7 @@ ASTUnaryExpression UnaryExpression(boolean isUnarySign) #UnaryExpression(>1) :
(
( ("+" ) { sb.append(isUnarySign ? " +" : " + "); }
| ("-" ) { sb.append(isUnarySign ? " -" : " - "); }
| (<PRIOR>) { sb.append(" PRIOR "); }
)
(simpleNode = UnaryExpression(false) ) { sb.append(simpleNode.getImage()); }
|
@@ -3039,12 +3065,13 @@ ASTPrimaryPrefix PrimaryPrefix() :
(
// Note: AnalyticClause and WithinClause are only allowed for specific functions, but this grammar allows it for all functions.
LOOKAHEAD(FunctionName() "(") simpleNode = FunctionCall() [ AnalyticClause() ] [ WithinClause() ]
| LOOKAHEAD(Literal()) simpleNode = Literal() { sb.append(simpleNode.getImage()) ; }
| LOOKAHEAD(MultiSetCondition()) simpleNode = MultiSetCondition()
| LOOKAHEAD(TrimExpression()) simpleNode = TrimExpression() //SRT 20110613.3
| LOOKAHEAD(CaseExpression()) ( simpleNode =CaseExpression() ) { sb.append(simpleNode.getImage()) ; } //SRT 20110520
| LOOKAHEAD(ObjectExpression() ) ( simpleNode = ObjectExpression() ) { sb.append(simpleNode.getImage()) ; } //SRT 20110604
//| LOOKAHEAD(LikeExpression()) ( simpleNode = LikeExpression() ) { sb.append(simpleNode.getImage()) ; } //SRT 20110604
| LOOKAHEAD(SimpleExpression()) ( simpleNode = SimpleExpression() ) { sb.append(simpleNode.getImage()); }
| LOOKAHEAD(Literal()) ( simpleNode = Literal() ) { sb.append(simpleNode.getImage()) ; }
| ( simpleNode =Name() ) { sb.append(simpleNode.getImage()) ; }
| SelectStatement()
| LOOKAHEAD("(" <SELECT>) "(" SelectStatement() ")"
@@ -3069,20 +3096,20 @@ ASTPrimarySuffix PrimarySuffix() :
{ PLSQLNode suffixNode = null ; PLSQLNode arguments = null; StringBuilder sb = new StringBuilder() ; }
{
(
(".") {sb.append(".");} (suffixNode = QualifiedID() ) { sb.append(suffixNode.toString()) ; }
(".") {sb.append(".");} (suffixNode = QualifiedID() ) { sb.append(suffixNode.getImage()) ; }
//Bug 3535118 - Method call may include dblink
//String dblink = null;
//["@" dblink = qualifiedName() {sb.append("@"); sb.append(dblink); } ]
| //Possible dblink
("@") {sb.append("@");} (suffixNode = QualifiedID() ) { sb.append(suffixNode.toString()) ; }
("@") {sb.append("@");} (suffixNode = QualifiedID() ) { sb.append(suffixNode.getImage()) ; }
| //Possible Date Time Expression - Bug 3553725
( <AT> {sb.append(" "); sb.append(token.image.toUpperCase());}
( <LOCAL> {sb.append(" "); sb.append(token.image.toUpperCase());}
| ( <TIME> <ZONE> {sb.append(" TIME ZONE");}
( <DBTIMEZONE> {sb.append(" "); sb.append(token.image.toUpperCase());}
| <SESSIONTIMEZONE> {sb.append(" "); sb.append(token.image.toUpperCase());}
| suffixNode = StringLiteral() {sb.append(" "); sb.append(suffixNode.toString());}
| suffixNode = Expression() {sb.append(" "); sb.append(suffixNode.toString());}
| suffixNode = StringLiteral() {sb.append(" "); sb.append(suffixNode.getImage());}
| suffixNode = Expression() {sb.append(" "); sb.append(suffixNode.getImage());}
)
)
)
@@ -6031,7 +6058,6 @@ ASTID ID(): {}
| <DATE> //RESERVED WORD
| <DECLARE> //SYNTAX
| <DECIMAL> //RESERVED WORD
//| <_DEFAULT> //RESERVED WORD
| <DELETE> //RESERVED WORD
| <DESC> //RESERVED WORD
//| <DISTINCT> //RESERVED WORD
@@ -29,12 +29,12 @@ public class ASTRegexpLikeCondition extends net.sourceforge.pmd.lang.plsql.ast.A
return this.matchParam;
}
public ASTID getSourceChar() {
return (ASTID) jjtGetChild(0);
public ASTSqlExpression getSourceChar() {
return (ASTSqlExpression) jjtGetChild(0);
}
public ASTStringLiteral getPattern() {
return (ASTStringLiteral) jjtGetChild(1);
public ASTSqlExpression getPattern() {
return (ASTSqlExpression) jjtGetChild(1);
}
}
/* JavaCC - OriginalChecksum=afb8806a0c67f95b736d6e8bc46def15 (do not edit this line) */
@@ -1061,4 +1061,9 @@ public class PLSQLParserVisitorAdapter implements PLSQLParserVisitor {
public Object visit(ASTErrorLoggingClause node, Object data) {
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTSimpleExpression node, Object data) {
return visit((PLSQLNode) node, data);
}
}
@@ -1155,6 +1155,11 @@ public abstract class AbstractPLSQLRule extends AbstractRule implements PLSQLPar
return visit((PLSQLNode) node, data);
}
@Override
public Object visit(ASTSimpleExpression node, Object data) {
return visit((PLSQLNode) node, data);
}
/*
* Treat all Executable Code
*/
@@ -5,6 +5,7 @@
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;
@@ -22,6 +23,22 @@ public class SelectExpressionsTest extends AbstractPLSQLParserTst {
Assert.assertNotNull(input);
}
@Test
public void parseSelectSimpleExpression() throws Exception {
String code = IOUtils.toString(this.getClass().getResourceAsStream("SelectSimpleExpression.pls"),
StandardCharsets.UTF_8);
ASTInput input = parsePLSQL(code);
Assert.assertNotNull(input);
List<ASTSimpleExpression> simpleExpressions = input.findDescendantsOfType(ASTSimpleExpression.class);
Assert.assertEquals(1, simpleExpressions.size());
ASTSimpleExpression exp = simpleExpressions.get(0);
Assert.assertEquals("e.first_name", exp.getImage());
Assert.assertEquals(2, exp.jjtGetNumChildren());
Assert.assertEquals(ASTTableName.class, exp.jjtGetChild(0).getClass());
Assert.assertEquals(ASTColumn.class, exp.jjtGetChild(1).getClass());
}
@Test
public void parseSelectCount() throws Exception {
String code = IOUtils.toString(this.getClass().getResourceAsStream("SelectCount.pls"),
@@ -92,7 +92,7 @@ public class WhereClauseTest extends AbstractPLSQLParserTst {
StandardCharsets.UTF_8);
ASTInput input = parsePLSQL(code);
List<ASTRegexpLikeCondition> regexps = input.findDescendantsOfType(ASTRegexpLikeCondition.class);
Assert.assertEquals(2, regexps.size());
Assert.assertEquals(3, 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,12 @@
--
-- Expressions in the SelectList
--
BEGIN
SELECT e.first_name
INTO test
FROM employees e;
END;
/
@@ -16,5 +16,15 @@ INTO test
FROM employees
WHERE REGEXP_LIKE (last_name, '([aeiou])\1', 'i')
ORDER BY last_name;
SELECT e.first_name || ',' || e.last_name
INTO test
FROM employees e
WHERE REGEXP_LIKE(
e.last_name || ',' || e.first_name,
NVL(:search, e.last_name || ',' || e.first_name),
'i')
AND NVL(:selected, 0) = 0;
END;
/