Skip to content

Commit

Permalink
IN() with complex expressions (#1384)
Browse files Browse the repository at this point in the history
* IN() with Complex Expressions

Fixes #905
Allow Complex Expressions and multiple SubSelects for the IN() Expression

* Tune the Test Coverage

* Remove unused import

* Reset TEST status
  • Loading branch information
manticore-projects authored Nov 1, 2021
1 parent 8c1eba2 commit c423224
Show file tree
Hide file tree
Showing 10 changed files with 105 additions and 87 deletions.
57 changes: 54 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,63 @@ jacocoTestReport {
jacocoTestCoverageVerification {
violationRules {
rule {
//element = 'CLASS'
limit {
minimum = JavaVersion.current().isJava8() // for any reason, different results
? 0.83 // depending on the Java Version
: 0.842
minimum = 0.84
}
excludes = [
'net.sf.jsqlparser.util.validation.*',
'net.sf.jsqlparser.**.*Adapter',
'net.sf.jsqlparser.parser.JJTCCJSqlParserState',
'net.sf.jsqlparser.parser.TokenMgrError',
'net.sf.jsqlparser.parser.StreamProvider',
'net.sf.jsqlparser.parser.CCJSqlParserTokenManager',
'net.sf.jsqlparser.parser.ParseException',
'net.sf.jsqlparser.parser.SimpleNode',
'net.sf.jsqlparser.parser.SimpleCharStream',
'net.sf.jsqlparser.parser.StringProvider',
]
}
rule {
//element = 'CLASS'
limit {
counter = 'LINE'
value = 'MISSEDCOUNT'
maximum = 5458
}
excludes = [
'net.sf.jsqlparser.util.validation.*',
'net.sf.jsqlparser.**.*Adapter',
'net.sf.jsqlparser.parser.JJTCCJSqlParserState',
'net.sf.jsqlparser.parser.TokenMgrError',
'net.sf.jsqlparser.parser.StreamProvider',
'net.sf.jsqlparser.parser.CCJSqlParserTokenManager',
'net.sf.jsqlparser.parser.ParseException',
'net.sf.jsqlparser.parser.SimpleNode',
'net.sf.jsqlparser.parser.SimpleCharStream',
'net.sf.jsqlparser.parser.StringProvider',
]
}
// rule {
// element = 'CLASS'
// limit {
// counter = 'LINE'
// value = 'MISSEDRATIO'
// maximum = 0.3
// }
// excludes = [
// 'net.sf.jsqlparser.util.validation.*',
// 'net.sf.jsqlparser.**.*Adapter',
// 'net.sf.jsqlparser.parser.JJTCCJSqlParserState',
// 'net.sf.jsqlparser.parser.TokenMgrError',
// 'net.sf.jsqlparser.parser.StreamProvider',
// 'net.sf.jsqlparser.parser.CCJSqlParserTokenManager',
// 'net.sf.jsqlparser.parser.ParseException',
// 'net.sf.jsqlparser.parser.SimpleNode',
// 'net.sf.jsqlparser.parser.SimpleCharStream',
// 'net.sf.jsqlparser.parser.StringProvider',
// ]
// }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,6 @@ public void visit(InExpression expr) {
expr.getRightExpression().accept(this);
} else if (expr.getRightItemsList() != null) {
expr.getRightItemsList().accept(this);
} else {
expr.getMultiExpressionList().accept(this);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ public class InExpression extends ASTNodeAccessImpl implements Expression, Suppo
private ItemsList rightItemsList;
private boolean not = false;
private Expression rightExpression;
private MultiExpressionList multiExpressionList;

private int oldOracleJoinSyntax = NO_ORACLE_JOIN;

public InExpression() {
Expand Down Expand Up @@ -105,21 +103,12 @@ public String toString() {
if (not) {
statementBuilder.append("NOT ");
}

statementBuilder.append("IN ");

if (multiExpressionList != null) {
statementBuilder.append("(");
statementBuilder.append(multiExpressionList);
statementBuilder.append(")");
if (rightExpression == null) {
statementBuilder.append(rightItemsList);
} else {
if (rightExpression == null) {
statementBuilder.append(rightItemsList);
} else {
statementBuilder.append(rightExpression);
}
statementBuilder.append(rightExpression);
}

return statementBuilder.toString();
}

Expand All @@ -135,24 +124,11 @@ public void setOraclePriorPosition(int priorPosition) {
}
}

public MultiExpressionList getMultiExpressionList() {
return multiExpressionList;
}

public void setMultiExpressionList(MultiExpressionList multiExpressionList) {
this.multiExpressionList = multiExpressionList;
}

public InExpression withRightExpression(Expression rightExpression) {
this.setRightExpression(rightExpression);
return this;
}

public InExpression withMultiExpressionList(MultiExpressionList multiExpressionList) {
this.setMultiExpressionList(multiExpressionList);
return this;
}

@Override
public InExpression withOldOracleJoinSyntax(int oldOracleJoinSyntax) {
this.setOldOracleJoinSyntax(oldOracleJoinSyntax);
Expand Down
2 changes: 0 additions & 2 deletions src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -242,8 +242,6 @@ public void visit(InExpression inExpression) {
inExpression.getRightExpression().accept(this);
} else if (inExpression.getRightItemsList() != null) {
inExpression.getRightItemsList().accept(this);
} else {
inExpression.getMultiExpressionList().accept(this);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -186,41 +186,13 @@ public void visit(InExpression inExpression) {
buffer.append(" NOT");
}
buffer.append(" IN ");

if (inExpression.getMultiExpressionList() != null) {
parseMultiExpressionList(inExpression);
if (inExpression.getRightExpression() != null) {
inExpression.getRightExpression().accept(this);
} else {
if (inExpression.getRightExpression() != null) {
inExpression.getRightExpression().accept(this);
} else {
inExpression.getRightItemsList().accept(this);
}
inExpression.getRightItemsList().accept(this);
}
}

/**
* Produces a multi-expression in clause: {@code ((a, b), (c, d))}
*/
private void parseMultiExpressionList(InExpression inExpression) {
MultiExpressionList multiExprList = inExpression.getMultiExpressionList();
buffer.append("(");
for (Iterator<ExpressionList> it = multiExprList.getExprList().iterator(); it.hasNext();) {
buffer.append("(");
for (Iterator<Expression> iter = it.next().getExpressions().iterator(); iter.hasNext();) {
Expression expression = iter.next();
expression.accept(this);
if (iter.hasNext()) {
buffer.append(", ");
}
}
buffer.append(")");
if (it.hasNext()) {
buffer.append(", ");
}
}
buffer.append(")");
}

@Override
public void visit(FullTextSearch fullTextSearch) {
// Build a list of matched columns
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,6 @@ public void visit(InExpression inExpression) {
validateFeature(c, Feature.oracleOldJoinSyntax);
}
}

validateOptionalMultiExpressionList(inExpression.getMultiExpressionList());
validateOptionalExpression(inExpression.getRightExpression(), this);
validateOptionalItemsList(inExpression.getRightItemsList());
}
Expand Down
22 changes: 9 additions & 13 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -2924,7 +2924,7 @@ Expression XorExpression():
}
{
left=OrExpression() { result = left; }
(
( LOOKAHEAD(2)
<K_XOR>
right=OrExpression()
{
Expand All @@ -2943,7 +2943,7 @@ Expression OrExpression():
}
{
left=AndExpression() { result = left; }
(
( LOOKAHEAD(2)
<K_OR>
right=AndExpression()
{
Expand Down Expand Up @@ -2973,7 +2973,7 @@ Expression AndExpression() :
)
{ result = left; }

(
( LOOKAHEAD(2)
{ boolean useOperator = false; }
(<K_AND> | <K_AND_OPERATOR> {useOperator=true;} )
(
Expand Down Expand Up @@ -3100,7 +3100,7 @@ Expression InExpression() #InExpression :
{
InExpression result = new InExpression();
ItemsList leftItemsList = null;
ItemsList rightItemsList = null;
ExpressionList rightItemsList = null;
Expression leftExpression = null;
Expression rightExpression = null;
Token token;
Expand All @@ -3113,17 +3113,13 @@ Expression InExpression() #InExpression :

[<K_NOT> { result.setNot(true); } ] <K_IN>
(
// syntactic lookahead for a multi expression list, ie: ((a,b),(c,d))
LOOKAHEAD(3) multiExpressionList = MultiInExpressions()
| LOOKAHEAD(3) rightExpression = Function()
| LOOKAHEAD(2) token=<S_CHAR_LITERAL> { rightExpression = new StringValue(token.image); }
| LOOKAHEAD(3) "(" (LOOKAHEAD(3) rightItemsList=SubSelect() | rightItemsList=SimpleExpressionList(true) )")"
| rightExpression = SimpleExpression()
LOOKAHEAD(2) token=<S_CHAR_LITERAL> { result.setRightExpression(new StringValue(token.image)); }
| LOOKAHEAD(3) rightExpression = Function() { result.setRightExpression(rightExpression); }
| LOOKAHEAD( "(" ComplexExpressionList() ")" ) "(" rightItemsList=ComplexExpressionList() { result.setRightItemsList(rightItemsList.withBrackets(true) ); } ")"
| LOOKAHEAD(3) "(" rightExpression = SubSelect() { result.setRightExpression( ((SubSelect) rightExpression).withUseBrackets(true) ); } ")"
| LOOKAHEAD(2) rightExpression = SimpleExpression() { result.setRightExpression(rightExpression); }
)
{
result.setRightItemsList(rightItemsList);
result.setRightExpression(rightExpression);
result.setMultiExpressionList(multiExpressionList);
linkAST(result,jjtThis);
return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
import java.util.List;

import net.sf.jsqlparser.expression.operators.conditional.XorExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.statement.select.SubSelect;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
Expand All @@ -24,7 +26,6 @@
import static org.junit.Assert.fail;
import net.sf.jsqlparser.JSQLParserException;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.PlainSelect;
Expand Down Expand Up @@ -72,7 +73,7 @@ public void visit(InExpression expr) {
});

assertTrue(exprList.get(0) instanceof Expression);
assertTrue(exprList.get(1) instanceof ItemsList);
assertTrue(exprList.get(1) instanceof ExpressionList);
}

@Test
Expand All @@ -88,12 +89,12 @@ public void testInExpression() throws JSQLParserException {
public void visit(InExpression expr) {
super.visit(expr);
exprList.add(expr.getLeftExpression());
exprList.add(expr.getRightItemsList());
exprList.add(expr.getRightExpression());
}
});

assertTrue(exprList.get(0) instanceof RowConstructor);
assertTrue(exprList.get(1) instanceof ItemsList);
assertTrue(exprList.get(1) instanceof SubSelect);
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ public void testParseAndBuild() throws JSQLParserException {
)).withRightExpression(
new InExpression()
.withLeftExpression(new Column(asList("t1", "col3")))
.withRightItemsList(new ExpressionList().addExpressions(new StringValue("A"))));
.withRightItemsList(new ExpressionList(new StringValue("A"))));

Select select = new Select().withSelectBody(new PlainSelect().addSelectItems(new AllColumns()).withFromItem(t1)
.addJoins(new Join().withRightItem(t2)
.withOnExpression(
Expand Down Expand Up @@ -95,8 +96,7 @@ public void testParseAndBuildForXOR() throws JSQLParserException {
.withRightExpression(
new InExpression()
.withLeftExpression(new Column(asList("t1", "col3")))
.withRightItemsList(new ExpressionList()
.addExpressions(new StringValue("B"), new StringValue("C")))))
.withRightItemsList(new ExpressionList(new StringValue("B"), new StringValue("C")))))
.withRightExpression(new Column(asList("t2", "col4")));

Select select = new Select().withSelectBody(new PlainSelect().addSelectItems(new AllColumns()).withFromItem(t1)
Expand Down
28 changes: 28 additions & 0 deletions src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -4889,4 +4889,32 @@ public void testCanCallSubSelectOnWithItemEvenIfNotSetIssue1369() {
WithItem item = new WithItem();
assertThat(item.getSubSelect()).isNull();
}

@Test
public void testComplexInExpressionIssue905() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed(
"select * " +
"from table_a " +
"where other_id in (" +
" (select id from table_b where name like '%aa%')" +
" , (select id from table_b where name like '%bb%')" +
")", true);

assertSqlCanBeParsedAndDeparsed(
"select * from v.e\n" +
"where\n" +
"\tcid <> rid\n" +
"\tand rid not in\n" +
"\t(\n" +
"\t\t(select distinct rid from v.s )\n" +
"\t\tunion\n" +
"\t\t(select distinct rid from v.p )\n" +
"\t)\n" +
"\tand \"timestamp\" <= 1298505600000", true);

assertSqlCanBeParsedAndDeparsed(
"select * " +
"from table_a " +
"where (a, b, c) in ((1, 2, 3), (3, 4, 5))", true);
}
}

0 comments on commit c423224

Please sign in to comment.