Skip to content

Commit

Permalink
Support multiple lists for an IN clause (#997)
Browse files Browse the repository at this point in the history
* visual

* wip

* cleanup n test

* polish

* lookahead

Co-authored-by: Jan Monterrubio <Jan.Monterrubio@Cerner.com>
  • Loading branch information
AnEmortalKid and Jan Monterrubio authored Jun 20, 2020
1 parent d34c885 commit 5de4ae5
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ 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;

Expand Down Expand Up @@ -95,8 +96,33 @@ private String getLeftExpressionString() {

@Override
public String toString() {
return (leftExpression == null ? leftItemsList : getLeftExpressionString()) + " "
+ (not ? "NOT " : "") + "IN " + (rightExpression == null ? rightItemsList : rightExpression) + "";
StringBuilder statementBuilder = new StringBuilder();
if (leftExpression == null) {
statementBuilder.append(leftItemsList);
} else {
statementBuilder.append(getLeftExpressionString());
}

statementBuilder.append(" ");
if (not) {
statementBuilder.append("NOT ");
}

statementBuilder.append("IN ");

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

return statementBuilder.toString();
}

@Override
Expand All @@ -110,4 +136,12 @@ public void setOraclePriorPosition(int priorPosition) {
throw new IllegalArgumentException("unexpected prior for oracle found");
}
}

public MultiExpressionList getMultiExpressionList() {
return multiExpressionList;
}

public void setMultiExpressionList(MultiExpressionList multiExpressionList) {
this.multiExpressionList = multiExpressionList;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -201,11 +201,38 @@ public void visit(InExpression inExpression) {
}
buffer.append(" IN ");

if (inExpression.getRightExpression() != null) {
inExpression.getRightExpression().accept(this);
if (inExpression.getMultiExpressionList() != null) {
parseMultiExpressionList(inExpression);
} else {
inExpression.getRightItemsList().accept(this);
if (inExpression.getRightExpression() != null) {
inExpression.getRightExpression().accept(this);
} else {
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
Expand Down
42 changes: 39 additions & 3 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -2543,6 +2543,8 @@ Expression InExpression() #InExpression :
Expression leftExpression = null;
Expression rightExpression = null;
Token token;
MultiExpressionList multiExpressionList = null;
ExpressionList expressionList = null;
}
{
( LOOKAHEAD(3) "(" (
Expand All @@ -2557,17 +2559,51 @@ Expression InExpression() #InExpression :
[ "(" "+" ")" { result.setOldOracleJoinSyntax(EqualsTo.ORACLE_JOIN_RIGHT); } ]
)
[<K_NOT> { result.setNot(true); } ] <K_IN>
( rightExpression = Function()
| token=<S_CHAR_LITERAL> { rightExpression = new StringValue(token.image); }
| "(" (LOOKAHEAD(3) rightItemsList=SubSelect() | rightItemsList=SimpleExpressionList() ) ")")
(
// syntactic lookahead for a multi expression list, ie: ((a,b),(c,d))
LOOKAHEAD(3) multiExpressionList = MultiInExpressions()
| rightExpression = Function()
| token=<S_CHAR_LITERAL> { rightExpression = new StringValue(token.image); }
| "(" (LOOKAHEAD(3) rightItemsList=SubSelect() | rightItemsList=SimpleExpressionList() )")"
)
{
result.setRightItemsList(rightItemsList);
result.setRightExpression(rightExpression);
result.setMultiExpressionList(multiExpressionList);
linkAST(result,jjtThis);
return result;
}
}

MultiExpressionList MultiInExpressions():
{
MultiExpressionList multiExpressionList = null;
ExpressionList expressionList = null;
}
{
"(" "("
expressionList=SimpleExpressionList() {
if(multiExpressionList == null) {
multiExpressionList = new MultiExpressionList();
}
multiExpressionList.addExpressionList(expressionList);
}
// potentially additional expression lists
( LOOKAHEAD(3)
")" "," "(" expressionList=SimpleExpressionList()
{
if(multiExpressionList == null) {
multiExpressionList = new MultiExpressionList();
}
multiExpressionList.addExpressionList(expressionList);
}
)*
")" ")"
{
return multiExpressionList;
}
}

Expression Between(Expression leftExpression) :
{
Between result = new Between();
Expand Down
41 changes: 36 additions & 5 deletions src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2319,11 +2319,42 @@ public void testMultiValueIn2() throws JSQLParserException {
assertSqlCanBeParsedAndDeparsed(stmt);
}

// @Test
// public void testMultiValueIn3() throws JSQLParserException {
// String stmt = "SELECT * FROM mytable WHERE (SSN,SSM) IN (('11111111111111', '22222222222222'))";
// assertSqlCanBeParsedAndDeparsed(stmt);
// }
@Test
public void testMultiValueIn3() throws JSQLParserException {
String stmt = "SELECT * FROM mytable WHERE (SSN, SSM) IN (('11111111111111', '22222222222222'))";
assertSqlCanBeParsedAndDeparsed(stmt);
}

@Test
public void testMultiValueIn_withAnd() throws JSQLParserException {
String stmt = "SELECT * FROM mytable WHERE (SSN, SSM) IN (('11111111111111', '22222222222222')) AND 1 = 1";
assertSqlCanBeParsedAndDeparsed(stmt);
}

@Test
public void testMultiValueIn4() throws JSQLParserException {
String stmt = "SELECT * FROM mytable WHERE (a, b) IN ((1, 2), (3, 4), (5, 6), (7, 8))";
assertSqlCanBeParsedAndDeparsed(stmt);
}

@Test
public void testMultiValueInBinds() throws JSQLParserException {
String stmt = "SELECT * FROM mytable WHERE (a, b) IN ((?, ?), (?, ?))";
assertSqlCanBeParsedAndDeparsed(stmt);
}

@Test
public void testMultiValueNotInBinds() throws JSQLParserException {
String stmt = "SELECT * FROM mytable WHERE (a, b) NOT IN ((?, ?), (?, ?))";
assertSqlCanBeParsedAndDeparsed(stmt);
}

@Test
public void testMultiValueIn_NTuples() throws JSQLParserException {
String stmt = "SELECT * FROM mytable WHERE (a, b, c, d, e) IN ((1, 2, 3, 4, 5), (6, 7, 8, 9, 10), (11, 12, 13, 14, 15))";
assertSqlCanBeParsedAndDeparsed(stmt);
}

@Test
public void testPivot1() throws JSQLParserException {
String stmt = "SELECT * FROM mytable PIVOT (count(a) FOR b IN ('val1'))";
Expand Down

0 comments on commit 5de4ae5

Please sign in to comment.