Skip to content

Commit 5de4ae5

Browse files
AnEmortalKidJan Monterrubio
andauthored
Support multiple lists for an IN clause (#997)
* visual * wip * cleanup n test * polish * lookahead Co-authored-by: Jan Monterrubio <[email protected]>
1 parent d34c885 commit 5de4ae5

File tree

4 files changed

+141
-13
lines changed

4 files changed

+141
-13
lines changed

src/main/java/net/sf/jsqlparser/expression/operators/relational/InExpression.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ public class InExpression extends ASTNodeAccessImpl implements Expression, Suppo
2020
private ItemsList rightItemsList;
2121
private boolean not = false;
2222
private Expression rightExpression;
23+
private MultiExpressionList multiExpressionList;
2324

2425
private int oldOracleJoinSyntax = NO_ORACLE_JOIN;
2526

@@ -95,8 +96,33 @@ private String getLeftExpressionString() {
9596

9697
@Override
9798
public String toString() {
98-
return (leftExpression == null ? leftItemsList : getLeftExpressionString()) + " "
99-
+ (not ? "NOT " : "") + "IN " + (rightExpression == null ? rightItemsList : rightExpression) + "";
99+
StringBuilder statementBuilder = new StringBuilder();
100+
if (leftExpression == null) {
101+
statementBuilder.append(leftItemsList);
102+
} else {
103+
statementBuilder.append(getLeftExpressionString());
104+
}
105+
106+
statementBuilder.append(" ");
107+
if (not) {
108+
statementBuilder.append("NOT ");
109+
}
110+
111+
statementBuilder.append("IN ");
112+
113+
if (multiExpressionList != null) {
114+
statementBuilder.append("(");
115+
statementBuilder.append(multiExpressionList);
116+
statementBuilder.append(")");
117+
} else {
118+
if (rightExpression == null ) {
119+
statementBuilder.append(rightItemsList);
120+
} else {
121+
statementBuilder.append(rightExpression);
122+
}
123+
}
124+
125+
return statementBuilder.toString();
100126
}
101127

102128
@Override
@@ -110,4 +136,12 @@ public void setOraclePriorPosition(int priorPosition) {
110136
throw new IllegalArgumentException("unexpected prior for oracle found");
111137
}
112138
}
139+
140+
public MultiExpressionList getMultiExpressionList() {
141+
return multiExpressionList;
142+
}
143+
144+
public void setMultiExpressionList(MultiExpressionList multiExpressionList) {
145+
this.multiExpressionList = multiExpressionList;
146+
}
113147
}

src/main/java/net/sf/jsqlparser/util/deparser/ExpressionDeParser.java

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,38 @@ public void visit(InExpression inExpression) {
201201
}
202202
buffer.append(" IN ");
203203

204-
if (inExpression.getRightExpression() != null) {
205-
inExpression.getRightExpression().accept(this);
204+
if (inExpression.getMultiExpressionList() != null) {
205+
parseMultiExpressionList(inExpression);
206206
} else {
207-
inExpression.getRightItemsList().accept(this);
207+
if (inExpression.getRightExpression() != null) {
208+
inExpression.getRightExpression().accept(this);
209+
} else {
210+
inExpression.getRightItemsList().accept(this);
211+
}
212+
}
213+
}
214+
215+
/**
216+
* Produces a multi-expression in clause: {@code ((a, b), (c, d))}
217+
*/
218+
private void parseMultiExpressionList(InExpression inExpression) {
219+
MultiExpressionList multiExprList = inExpression.getMultiExpressionList();
220+
buffer.append("(");
221+
for (Iterator<ExpressionList> it = multiExprList.getExprList().iterator(); it.hasNext();) {
222+
buffer.append("(");
223+
for (Iterator<Expression> iter = it.next().getExpressions().iterator(); iter.hasNext();) {
224+
Expression expression = iter.next();
225+
expression.accept(this);
226+
if (iter.hasNext()) {
227+
buffer.append(", ");
228+
}
229+
}
230+
buffer.append(")");
231+
if (it.hasNext()) {
232+
buffer.append(", ");
233+
}
208234
}
235+
buffer.append(")");
209236
}
210237

211238
@Override

src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt

Lines changed: 39 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2543,6 +2543,8 @@ Expression InExpression() #InExpression :
25432543
Expression leftExpression = null;
25442544
Expression rightExpression = null;
25452545
Token token;
2546+
MultiExpressionList multiExpressionList = null;
2547+
ExpressionList expressionList = null;
25462548
}
25472549
{
25482550
( LOOKAHEAD(3) "(" (
@@ -2557,17 +2559,51 @@ Expression InExpression() #InExpression :
25572559
[ "(" "+" ")" { result.setOldOracleJoinSyntax(EqualsTo.ORACLE_JOIN_RIGHT); } ]
25582560
)
25592561
[<K_NOT> { result.setNot(true); } ] <K_IN>
2560-
( rightExpression = Function()
2561-
| token=<S_CHAR_LITERAL> { rightExpression = new StringValue(token.image); }
2562-
| "(" (LOOKAHEAD(3) rightItemsList=SubSelect() | rightItemsList=SimpleExpressionList() ) ")")
2562+
(
2563+
// syntactic lookahead for a multi expression list, ie: ((a,b),(c,d))
2564+
LOOKAHEAD(3) multiExpressionList = MultiInExpressions()
2565+
| rightExpression = Function()
2566+
| token=<S_CHAR_LITERAL> { rightExpression = new StringValue(token.image); }
2567+
| "(" (LOOKAHEAD(3) rightItemsList=SubSelect() | rightItemsList=SimpleExpressionList() )")"
2568+
)
25632569
{
25642570
result.setRightItemsList(rightItemsList);
25652571
result.setRightExpression(rightExpression);
2572+
result.setMultiExpressionList(multiExpressionList);
25662573
linkAST(result,jjtThis);
25672574
return result;
25682575
}
25692576
}
25702577

2578+
MultiExpressionList MultiInExpressions():
2579+
{
2580+
MultiExpressionList multiExpressionList = null;
2581+
ExpressionList expressionList = null;
2582+
}
2583+
{
2584+
"(" "("
2585+
expressionList=SimpleExpressionList() {
2586+
if(multiExpressionList == null) {
2587+
multiExpressionList = new MultiExpressionList();
2588+
}
2589+
multiExpressionList.addExpressionList(expressionList);
2590+
}
2591+
// potentially additional expression lists
2592+
( LOOKAHEAD(3)
2593+
")" "," "(" expressionList=SimpleExpressionList()
2594+
{
2595+
if(multiExpressionList == null) {
2596+
multiExpressionList = new MultiExpressionList();
2597+
}
2598+
multiExpressionList.addExpressionList(expressionList);
2599+
}
2600+
)*
2601+
")" ")"
2602+
{
2603+
return multiExpressionList;
2604+
}
2605+
}
2606+
25712607
Expression Between(Expression leftExpression) :
25722608
{
25732609
Between result = new Between();

src/test/java/net/sf/jsqlparser/statement/select/SelectTest.java

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2319,11 +2319,42 @@ public void testMultiValueIn2() throws JSQLParserException {
23192319
assertSqlCanBeParsedAndDeparsed(stmt);
23202320
}
23212321

2322-
// @Test
2323-
// public void testMultiValueIn3() throws JSQLParserException {
2324-
// String stmt = "SELECT * FROM mytable WHERE (SSN,SSM) IN (('11111111111111', '22222222222222'))";
2325-
// assertSqlCanBeParsedAndDeparsed(stmt);
2326-
// }
2322+
@Test
2323+
public void testMultiValueIn3() throws JSQLParserException {
2324+
String stmt = "SELECT * FROM mytable WHERE (SSN, SSM) IN (('11111111111111', '22222222222222'))";
2325+
assertSqlCanBeParsedAndDeparsed(stmt);
2326+
}
2327+
2328+
@Test
2329+
public void testMultiValueIn_withAnd() throws JSQLParserException {
2330+
String stmt = "SELECT * FROM mytable WHERE (SSN, SSM) IN (('11111111111111', '22222222222222')) AND 1 = 1";
2331+
assertSqlCanBeParsedAndDeparsed(stmt);
2332+
}
2333+
2334+
@Test
2335+
public void testMultiValueIn4() throws JSQLParserException {
2336+
String stmt = "SELECT * FROM mytable WHERE (a, b) IN ((1, 2), (3, 4), (5, 6), (7, 8))";
2337+
assertSqlCanBeParsedAndDeparsed(stmt);
2338+
}
2339+
2340+
@Test
2341+
public void testMultiValueInBinds() throws JSQLParserException {
2342+
String stmt = "SELECT * FROM mytable WHERE (a, b) IN ((?, ?), (?, ?))";
2343+
assertSqlCanBeParsedAndDeparsed(stmt);
2344+
}
2345+
2346+
@Test
2347+
public void testMultiValueNotInBinds() throws JSQLParserException {
2348+
String stmt = "SELECT * FROM mytable WHERE (a, b) NOT IN ((?, ?), (?, ?))";
2349+
assertSqlCanBeParsedAndDeparsed(stmt);
2350+
}
2351+
2352+
@Test
2353+
public void testMultiValueIn_NTuples() throws JSQLParserException {
2354+
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))";
2355+
assertSqlCanBeParsedAndDeparsed(stmt);
2356+
}
2357+
23272358
@Test
23282359
public void testPivot1() throws JSQLParserException {
23292360
String stmt = "SELECT * FROM mytable PIVOT (count(a) FOR b IN ('val1'))";

0 commit comments

Comments
 (0)