Skip to content

Commit 633641c

Browse files
committed
DuckDB: support the ==, ** and ~~ operators and chained signs
- `a == b` is `EqualsTo` with the `==` spelling. - `a ** b` is the new `Exponentiation`, above the multiplicative operators and left-associative, as in DuckDB. - `~~`, `~~*`, `~~~`, `!~~` and `!~~*` are `LIKE`, `ILIKE`, `GLOB` and their negations, kept as written. `a ~~ 'x'` used to parse as the regular expression match `a ~ (~'x')`. - `- -1`, `+-+1` and `~ ~a` nest signs. A sign is separated from an operand starting with the same sign so that `- -1` does not deparse as the comment `--1`, nor `~ ~a` as `~~a`.
1 parent 49b5ea9 commit 633641c

11 files changed

Lines changed: 255 additions & 14 deletions

File tree

‎src/main/java/net/sf/jsqlparser/expression/ExpressionVisitor.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor;
2020
import net.sf.jsqlparser.expression.operators.arithmetic.Concat;
2121
import net.sf.jsqlparser.expression.operators.arithmetic.Division;
22+
import net.sf.jsqlparser.expression.operators.arithmetic.Exponentiation;
2223
import net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision;
2324
import net.sf.jsqlparser.expression.operators.arithmetic.Modulo;
2425
import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication;
@@ -271,6 +272,12 @@ default void visit(Division division) {
271272

272273
<S> T visit(IntegerDivision integerDivision, S context);
273274

275+
<S> T visit(Exponentiation exponentiation, S context);
276+
277+
default void visit(Exponentiation exponentiation) {
278+
this.visit(exponentiation, null);
279+
}
280+
274281
default void visit(IntegerDivision integerDivision) {
275282
this.visit(integerDivision, null);
276283
}

‎src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor;
2323
import net.sf.jsqlparser.expression.operators.arithmetic.Concat;
2424
import net.sf.jsqlparser.expression.operators.arithmetic.Division;
25+
import net.sf.jsqlparser.expression.operators.arithmetic.Exponentiation;
2526
import net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision;
2627
import net.sf.jsqlparser.expression.operators.arithmetic.Modulo;
2728
import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication;
@@ -198,6 +199,11 @@ public <S> T visit(IntegerDivision integerDivision, S context) {
198199
return visitBinaryExpression(integerDivision, context);
199200
}
200201

202+
@Override
203+
public <S> T visit(Exponentiation exponentiation, S context) {
204+
return visitBinaryExpression(exponentiation, context);
205+
}
206+
201207
@Override
202208
public <S> T visit(Multiplication multiplication, S context) {
203209
return visitBinaryExpression(multiplication, context);

‎src/main/java/net/sf/jsqlparser/expression/SignedExpression.java‎

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,20 @@ public <T, S> T accept(ExpressionVisitor<T> expressionVisitor, S context) {
5454

5555
@Override
5656
public String toString() {
57-
return getSign() + expression.toString();
57+
String operand = expression.toString();
58+
return getSign() + (needsSpaceBefore(operand) ? " " : "") + operand;
59+
}
60+
61+
/**
62+
* Whether the sign must be separated from an operand starting with {@code firstChar}: "- -1"
63+
* must not become the line comment "--1", nor "~ ~a" the LIKE operator "~~".
64+
*/
65+
public boolean needsSpaceBefore(char firstChar) {
66+
return (sign == '-' || sign == '~') && firstChar == sign;
67+
}
68+
69+
private boolean needsSpaceBefore(String operand) {
70+
return !operand.isEmpty() && needsSpaceBefore(operand.charAt(0));
5871
}
5972

6073
public SignedExpression withSign(char sign) {
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/*-
2+
* #%L
3+
* JSQLParser library
4+
* %%
5+
* Copyright (C) 2004 - 2026 JSQLParser
6+
* %%
7+
* Dual licensed under GNU LGPL 2.1 or Apache License 2.0
8+
* #L%
9+
*/
10+
package net.sf.jsqlparser.expression.operators.arithmetic;
11+
12+
import net.sf.jsqlparser.expression.BinaryExpression;
13+
import net.sf.jsqlparser.expression.Expression;
14+
import net.sf.jsqlparser.expression.ExpressionVisitor;
15+
16+
/** DuckDB's {@code a ** b}, {@code a} raised to the power {@code b}. */
17+
public class Exponentiation extends BinaryExpression {
18+
19+
public Exponentiation() {}
20+
21+
public Exponentiation(Expression leftExpression, Expression rightExpression) {
22+
super(leftExpression, rightExpression);
23+
}
24+
25+
@Override
26+
public <T, S> T accept(ExpressionVisitor<T> expressionVisitor, S context) {
27+
return expressionVisitor.visit(this, context);
28+
}
29+
30+
@Override
31+
public String getStringExpression() {
32+
return "**";
33+
}
34+
35+
@Override
36+
public Exponentiation withLeftExpression(Expression arg0) {
37+
return (Exponentiation) super.withLeftExpression(arg0);
38+
}
39+
40+
@Override
41+
public Exponentiation withRightExpression(Expression arg0) {
42+
return (Exponentiation) super.withRightExpression(arg0);
43+
}
44+
}

‎src/main/java/net/sf/jsqlparser/expression/operators/relational/EqualsTo.java‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ public EqualsTo() {
1818
super("=");
1919
}
2020

21+
/** {@code operator} is {@code =} or DuckDB's {@code ==}. */
22+
public EqualsTo(String operator) {
23+
super(operator);
24+
}
25+
2126
public EqualsTo(Expression left, Expression right) {
2227
this();
2328
setLeftExpression(left);

‎src/main/java/net/sf/jsqlparser/expression/operators/relational/LikeExpression.java‎

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class LikeExpression extends BinaryExpression implements SupportsOldOracl
1818
private boolean not = false;
1919
private boolean useBinary = false;
2020
private Expression escapeExpression = null;
21+
private String likeOperator = null;
2122
private KeyWord likeKeyWord = KeyWord.LIKE;
2223

2324
public boolean isNot() {
@@ -48,8 +49,42 @@ public String getStringExpression() {
4849
return likeKeyWord.toString();
4950
}
5051

52+
/** DuckDB's and PostgreSQL's operator spelling, e.g. {@code !~~} for {@code NOT LIKE}. */
53+
public String getLikeOperator() {
54+
return likeOperator;
55+
}
56+
57+
/**
58+
* Sets the operator spelling and the matching keyword: {@code ~~} is {@code LIKE}, {@code ~~*}
59+
* {@code ILIKE}, {@code ~~~} {@code GLOB}, and a leading {@code !} negates.
60+
*/
61+
public LikeExpression setLikeOperator(String likeOperator) {
62+
boolean negated = likeOperator.startsWith("!");
63+
String operator = negated ? likeOperator.substring(1) : likeOperator;
64+
switch (operator) {
65+
case "~~":
66+
likeKeyWord = KeyWord.LIKE;
67+
break;
68+
case "~~*":
69+
likeKeyWord = KeyWord.ILIKE;
70+
break;
71+
case "~~~":
72+
likeKeyWord = KeyWord.GLOB;
73+
break;
74+
default:
75+
throw new IllegalArgumentException("Unknown LIKE operator: " + likeOperator);
76+
}
77+
this.not = negated;
78+
this.likeOperator = likeOperator;
79+
return this;
80+
}
81+
5182
@Override
5283
public String toString() {
84+
if (likeOperator != null) {
85+
return getLeftExpression() + " " + likeOperator + " " + getRightExpression()
86+
+ (escapeExpression != null ? " ESCAPE " + escapeExpression : "");
87+
}
5388
String retval = (oraclePriorPosition == ORACLE_PRIOR_START ? "PRIOR " : "")
5489
+ getLeftExpression() + " " + (not ? "NOT " : "")
5590
+ (likeKeyWord == KeyWord.SIMILAR_TO ? "SIMILAR TO" : likeKeyWord) + " "

‎src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor;
2828
import net.sf.jsqlparser.expression.operators.arithmetic.Concat;
2929
import net.sf.jsqlparser.expression.operators.arithmetic.Division;
30+
import net.sf.jsqlparser.expression.operators.arithmetic.Exponentiation;
3031
import net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision;
3132
import net.sf.jsqlparser.expression.operators.arithmetic.Modulo;
3233
import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication;
@@ -663,6 +664,12 @@ public <S> Void visit(IntegerDivision division, S context) {
663664
return null;
664665
}
665666

667+
@Override
668+
public <S> Void visit(Exponentiation exponentiation, S context) {
669+
visitBinaryExpression(exponentiation);
670+
return null;
671+
}
672+
666673
@Override
667674
public <S> Void visit(DoubleValue doubleValue, S context) {
668675

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

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@
9393
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor;
9494
import net.sf.jsqlparser.expression.operators.arithmetic.Concat;
9595
import net.sf.jsqlparser.expression.operators.arithmetic.Division;
96+
import net.sf.jsqlparser.expression.operators.arithmetic.Exponentiation;
9697
import net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision;
9798
import net.sf.jsqlparser.expression.operators.arithmetic.Modulo;
9899
import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication;
@@ -225,7 +226,7 @@ public <S> StringBuilder visit(OverlapsCondition overlapsCondition, S context) {
225226

226227
@Override
227228
public <S> StringBuilder visit(EqualsTo equalsTo, S context) {
228-
deparse(equalsTo, " = ", context);
229+
deparse(equalsTo, " " + equalsTo.getStringExpression() + " ", context);
229230
return builder;
230231
}
231232

@@ -241,6 +242,12 @@ public <S> StringBuilder visit(IntegerDivision division, S context) {
241242
return builder;
242243
}
243244

245+
@Override
246+
public <S> StringBuilder visit(Exponentiation exponentiation, S context) {
247+
deparse(exponentiation, " ** ", context);
248+
return builder;
249+
}
250+
244251
@Override
245252
public <S> StringBuilder visit(DoubleValue doubleValue, S context) {
246253
builder.append(doubleValue.toString());
@@ -425,7 +432,12 @@ public <S> StringBuilder visit(FullTextSearch fullTextSearch, S context) {
425432
@Override
426433
public <S> StringBuilder visit(SignedExpression signedExpression, S context) {
427434
builder.append(signedExpression.getSign());
435+
int operandStart = builder.length();
428436
signedExpression.getExpression().accept(this, context);
437+
if (builder.length() > operandStart
438+
&& signedExpression.needsSpaceBefore(builder.charAt(operandStart))) {
439+
builder.insert(operandStart, ' ');
440+
}
429441
return builder;
430442
}
431443

@@ -509,6 +521,16 @@ public <S> StringBuilder visit(JdbcParameter jdbcParameter, S context) {
509521

510522
@Override
511523
public <S> StringBuilder visit(LikeExpression likeExpression, S context) {
524+
if (likeExpression.getLikeOperator() != null) {
525+
likeExpression.getLeftExpression().accept(this, context);
526+
builder.append(" ").append(likeExpression.getLikeOperator()).append(" ");
527+
likeExpression.getRightExpression().accept(this, context);
528+
if (likeExpression.getEscape() != null) {
529+
builder.append(" ESCAPE ");
530+
likeExpression.getEscape().accept(this, context);
531+
}
532+
return builder;
533+
}
512534
String keywordStr = likeExpression.getLikeKeyWord() == LikeExpression.KeyWord.SIMILAR_TO
513535
? " SIMILAR TO"
514536
: likeExpression.getLikeKeyWord().toString();

‎src/main/java/net/sf/jsqlparser/util/validation/validator/ExpressionValidator.java‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@
9090
import net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor;
9191
import net.sf.jsqlparser.expression.operators.arithmetic.Concat;
9292
import net.sf.jsqlparser.expression.operators.arithmetic.Division;
93+
import net.sf.jsqlparser.expression.operators.arithmetic.Exponentiation;
9394
import net.sf.jsqlparser.expression.operators.arithmetic.IntegerDivision;
9495
import net.sf.jsqlparser.expression.operators.arithmetic.Modulo;
9596
import net.sf.jsqlparser.expression.operators.arithmetic.Multiplication;
@@ -199,6 +200,12 @@ public <S> Void visit(IntegerDivision division, S context) {
199200
return null;
200201
}
201202

203+
@Override
204+
public <S> Void visit(Exponentiation exponentiation, S context) {
205+
visitBinaryExpression(exponentiation, " ** ");
206+
return null;
207+
}
208+
202209
@Override
203210
public <S> Void visit(DoubleValue doubleValue, S context) {
204211
// nothing to validate

0 commit comments

Comments
 (0)