Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions src/main/java/net/sf/jsqlparser/expression/ArrayExpression.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class ArrayExpression extends ASTNodeAccessImpl implements Expression {
private Expression indexExpression;
private Expression startIndexExpression;
private Expression stopIndexExpression;
private Expression stepExpression;


public ArrayExpression() {
Expand Down Expand Up @@ -72,6 +73,19 @@ public void setStopIndexExpression(Expression stopIndexExpression) {
this.stopIndexExpression = stopIndexExpression;
}

public Expression getStepExpression() {
return stepExpression;
}

public void setStepExpression(Expression stepExpression) {
this.stepExpression = stepExpression;
}

public ArrayExpression withStepExpression(Expression stepExpression) {
this.setStepExpression(stepExpression);
return this;
}

@Override
public <T, S> T accept(ExpressionVisitor<T> expressionVisitor, S context) {
return expressionVisitor.visit(this, context);
Expand All @@ -82,11 +96,8 @@ public String toString() {
if (indexExpression != null) {
return objExpression.toString() + "[" + indexExpression.toString() + "]";
} else {
return objExpression.toString() + "[" +
(startIndexExpression == null ? "" : startIndexExpression.toString()) +
":" +
(stopIndexExpression == null ? "" : stopIndexExpression.toString()) +
"]";
return objExpression.toString() + "[" + RangeExpression.toString(startIndexExpression,
stopIndexExpression, stepExpression) + "]";
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -552,7 +552,7 @@
public <S> T visit(JsonExpression jsonExpr, S context) {
ArrayList<Expression> subExpressions = new ArrayList<>();
subExpressions.add(jsonExpr.getExpression());
subExpressions.addAll(jsonExpr.getIdents());

Check warning on line 555 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

View workflow job for this annotation

GitHub Actions / Maven Verify (windows-latest)

[deprecation] getIdents() in JsonExpression has been deprecated

Check warning on line 555 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

View workflow job for this annotation

GitHub Actions / Maven Verify (ubuntu-latest)

[deprecation] getIdents() in JsonExpression has been deprecated

Check warning on line 555 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

View workflow job for this annotation

GitHub Actions / Maven Verify (macos-latest)

[deprecation] getIdents() in JsonExpression has been deprecated
return visitExpressions(jsonExpr, context, subExpressions);
}

Expand Down Expand Up @@ -715,6 +715,9 @@
if (arrayExpression.getStopIndexExpression() != null) {
subExpressions.add(arrayExpression.getStopIndexExpression());
}
if (arrayExpression.getStepExpression() != null) {
subExpressions.add(arrayExpression.getStepExpression());
}
return visitExpressions(arrayExpression, context, subExpressions);
}

Expand Down Expand Up @@ -814,7 +817,7 @@

@Override
public <S> T visit(ConnectByRootOperator connectByRootOperator, S context) {
return connectByRootOperator.getColumn().accept(this, context);

Check warning on line 820 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

View workflow job for this annotation

GitHub Actions / Maven Verify (windows-latest)

[deprecation] getColumn() in ConnectByRootOperator has been deprecated

Check warning on line 820 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

View workflow job for this annotation

GitHub Actions / Maven Verify (ubuntu-latest)

[deprecation] getColumn() in ConnectByRootOperator has been deprecated

Check warning on line 820 in src/main/java/net/sf/jsqlparser/expression/ExpressionVisitorAdapter.java

View workflow job for this annotation

GitHub Actions / Maven Verify (macos-latest)

[deprecation] getColumn() in ConnectByRootOperator has been deprecated
}

@Override
Expand Down Expand Up @@ -874,7 +877,7 @@
@Override
public <S> T visit(RangeExpression rangeExpression, S context) {
return visitExpressions(rangeExpression, context, rangeExpression.getStartExpression(),
rangeExpression.getEndExpression());
rangeExpression.getEndExpression(), rangeExpression.getStepExpression());
}

@Override
Expand Down
36 changes: 35 additions & 1 deletion src/main/java/net/sf/jsqlparser/expression/RangeExpression.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
public class RangeExpression extends ASTNodeAccessImpl implements Expression {
private Expression startExpression;
private Expression endExpression;
private Expression stepExpression;

public RangeExpression(Expression startExpression, Expression endExpression) {
this.startExpression = startExpression;
Expand All @@ -38,9 +39,42 @@ public RangeExpression setEndExpression(Expression endExpression) {
return this;
}

public Expression getStepExpression() {
return stepExpression;
}

public RangeExpression setStepExpression(Expression stepExpression) {
this.stepExpression = stepExpression;
return this;
}

public RangeExpression withStepExpression(Expression stepExpression) {
return setStepExpression(stepExpression);
}

/**
* Renders {@code [start]:[end][:step]}, a missing end before a step as DuckDB's {@code -}.
*/
public static String toString(Expression start, Expression end, Expression step) {
StringBuilder builder = new StringBuilder();
if (start != null) {
builder.append(start);
}
builder.append(":");
if (end != null) {
builder.append(end);
} else if (step != null) {
builder.append("-");
}
if (step != null) {
builder.append(":").append(step);
}
return builder.toString();
}

@Override
public String toString() {
return startExpression + ":" + endExpression;
return toString(startExpression, endExpression, stepExpression);
}

@Override
Expand Down
11 changes: 9 additions & 2 deletions src/main/java/net/sf/jsqlparser/util/TablesNamesFinder.java
Original file line number Diff line number Diff line change
Expand Up @@ -350,8 +350,12 @@ public <S> Void visit(TrimFunction trimFunction, S context) {

@Override
public <S> Void visit(RangeExpression rangeExpression, S context) {
rangeExpression.getStartExpression().accept(this, context);
rangeExpression.getEndExpression().accept(this, context);
for (Expression bound : new Expression[] {rangeExpression.getStartExpression(),
rangeExpression.getEndExpression(), rangeExpression.getStepExpression()}) {
if (bound != null) {
bound.accept(this, context);
}
}
return null;
}

Expand Down Expand Up @@ -2240,6 +2244,9 @@ public <S> Void visit(ArrayExpression array, S context) {
if (array.getStopIndexExpression() != null) {
array.getStopIndexExpression().accept(this, context);
}
if (array.getStepExpression() != null) {
array.getStepExpression().accept(this, context);
}
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -843,12 +843,27 @@ public void visit(TrimFunction trimFunction) {

@Override
public <S> StringBuilder visit(RangeExpression rangeExpression, S context) {
rangeExpression.getStartExpression().accept(this, context);
builder.append(":");
rangeExpression.getEndExpression().accept(this, context);
appendSlice(rangeExpression.getStartExpression(), rangeExpression.getEndExpression(),
rangeExpression.getStepExpression(), context);
return builder;
}

private <S> void appendSlice(Expression start, Expression end, Expression step, S context) {
if (start != null) {
start.accept(this, context);
}
builder.append(":");
if (end != null) {
end.accept(this, context);
} else if (step != null) {
builder.append("-");
}
if (step != null) {
builder.append(":");
step.accept(this, context);
}
}

@Override
public <S> StringBuilder visit(TernaryExpression ternaryExpression, S context) {
ternaryExpression.getCondition().accept(this, context);
Expand Down Expand Up @@ -1645,13 +1660,8 @@ public <S> StringBuilder visit(ArrayExpression array, S context) {
if (array.getIndexExpression() != null) {
array.getIndexExpression().accept(this, context);
} else {
if (array.getStartIndexExpression() != null) {
array.getStartIndexExpression().accept(this, context);
}
builder.append(":");
if (array.getStopIndexExpression() != null) {
array.getStopIndexExpression().accept(this, context);
}
appendSlice(array.getStartIndexExpression(), array.getStopIndexExpression(),
array.getStepExpression(), context);
}

builder.append("]");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1034,6 +1034,9 @@ public <S> Void visit(ArrayExpression array, S context) {
if (array.getStopIndexExpression() != null) {
array.getStopIndexExpression().accept(this, context);
}
if (array.getStepExpression() != null) {
array.getStepExpression().accept(this, context);
}
return null;
}

Expand Down Expand Up @@ -1200,8 +1203,12 @@ public <S> Void visit(TrimFunction trimFunction, S context) {

@Override
public <S> Void visit(RangeExpression rangeExpression, S context) {
rangeExpression.getStartExpression().accept(this, context);
rangeExpression.getEndExpression().accept(this, context);
for (Expression bound : new Expression[] {rangeExpression.getStartExpression(),
rangeExpression.getEndExpression(), rangeExpression.getStepExpression()}) {
if (bound != null) {
bound.accept(this, context);
}
}
return null;
}

Expand Down
126 changes: 106 additions & 20 deletions src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,32 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
// and must not be taken as the JSON path operator
private int mapKeyDepth = 0;

// opening brackets of the enclosing subscripts: a ":" at the top level of a subscript
// separates the bounds of a slice such as l[1:3] and is not the JSON path operator
private final Deque<Token> subscriptOpenings = new ArrayDeque<Token>();

/**
* Whether the pending ":" is at the top level of the innermost subscript, i.e. not nested
* in brackets, braces, parentheses or a CASE expression opened inside that subscript.
*/
private boolean isSliceColonAhead() {
Token colon = getToken(1);
if (subscriptOpenings.isEmpty() || !":".equals(colon.image)) {
return false;
}
int depth = 0;
for (Token t = subscriptOpenings.peek().next; t != null && t != colon; t = t.next) {
if (t.kind == K_CASE || "(".equals(t.image) || "[".equals(t.image)
|| "{".equals(t.image)) {
depth++;
} else if (t.kind == K_END || ")".equals(t.image) || "]".equals(t.image)
|| "}".equals(t.image)) {
depth--;
}
}
return depth == 0;
}

private Expression createOutputArgument(Expression argument) throws ParseException {
Expression value = argument;
if (argument instanceof VariableAssignment) {
Expand Down Expand Up @@ -1571,6 +1597,10 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
|| next.kind >= MIN_NON_RESERVED_WORD && next.kind <= MAX_NON_RESERVED_WORD;
}

private static boolean isSliceDelimiter(Token token) {
return ":".equals(token.image) || "]".equals(token.image) || ",".equals(token.image);
}

private boolean isAliasAhead() {
if (isAsOfJoinAhead() || isSemiAntiJoinAhead() || isApproxNearestAhead()) {
return false;
Expand Down Expand Up @@ -6884,7 +6914,7 @@ Column Column() #Column :
// @todo: we better should return a SEQUENCE instead of a COLUMN
[ LOOKAHEAD(2) "." <K_NEXTVAL> { data.getNames().add("nextval"); } ]

[ LOOKAHEAD(2) arrayConstructor = ArrayConstructor(false) ]
[ LOOKAHEAD(2) arrayConstructor = Subscript() ]
{
Column col = createColumn(data.getNames(), data.getDelimiters());
if (tk != null) { col.withCommentText(tk.image); }
Expand Down Expand Up @@ -11341,29 +11371,85 @@ Expression AddChain():

Expression ArrayExpression(Expression obj): {
Expression expr;
} {
expr = ArraySubscript(obj)
( LOOKAHEAD(2) expr = ArraySubscript(expr) )*
{ return expr; }
}

ArrayExpression ArraySubscript(Expression obj): {
Expression idxExpr = null;
Expression startExpr = null;
Expression stopExpr = null;
RangeExpression slice = null;
Token opening;
} {
"["
[LOOKAHEAD(3) idxExpr = ArrayIndexExpression()]
opening="["
try {
{ subscriptOpenings.push(opening); }
[ LOOKAHEAD(3, { !":".equals(getToken(1).image) }) idxExpr = ArrayIndexExpression() ]
[ slice = SliceTail(idxExpr) ]
} finally {
subscriptOpenings.pop();
}
"]"
{
return slice == null
? new ArrayExpression(obj, idxExpr, null, null)
: new ArrayExpression(obj, null, slice.getStartExpression(),
slice.getEndExpression()).withStepExpression(slice.getStepExpression());
}
}

/**
* The rest of a slice after its optional start: {@code :[stop][:[step]]}. As in DuckDB, a
* {@code -} stop before a step stands for the end of the list, as in {@code l[1:-:2]}.
*/
RangeExpression SliceTail(Expression start): {
Expression stop = null;
Expression step = null;
} {
":"
[
LOOKAHEAD("-" ":") "-"
|
LOOKAHEAD({ !isSliceDelimiter(getToken(1)) }) stop = Expression()
]
[
":" [ LOOKAHEAD({ !isSliceDelimiter(getToken(1)) }) step = Expression() ]
]
{ return new RangeExpression(start, stop).withStepExpression(step); }
}

/** A subscript of a column, such as {@code col[1]}, {@code col[1, 2]} or {@code col[1:3]}. */
ArrayConstructor Subscript() : {
ExpressionList<Expression> expList = new ExpressionList<Expression>();
Expression exp;
Token opening;
} {
opening="["
try {
{ subscriptOpenings.push(opening); }
[
(":" { startExpr=idxExpr; idxExpr=null; })
[stopExpr = SimpleExpression()]
exp=SubscriptElement() { expList.add(exp); }
( "," exp=SubscriptElement() { expList.add(exp); } )*
]
} finally {
subscriptOpenings.pop();
}
"]"
{ expr = new ArrayExpression(obj, idxExpr, startExpr, stopExpr); }
{ return new ArrayConstructor(expList, false); }
}

Expression SubscriptElement():
{
Expression expression = null;
}
{
(
LOOKAHEAD(2) "["
[LOOKAHEAD(3) idxExpr = ArrayIndexExpression()]
[
(":" { startExpr=idxExpr; idxExpr=null; })
[stopExpr = SimpleExpression()]
]
"]"
{ expr = new ArrayExpression(expr, idxExpr, startExpr, stopExpr); }
)*
{ return expr; }
LOOKAHEAD({ ":".equals(getToken(1).image) }) expression=SliceTail(null)
|
expression=Expression() [ expression=SliceTail(expression) ]
)
{ return expression; }
}

/**
Expand Down Expand Up @@ -11618,7 +11704,7 @@ Expression PrimaryExpression() #PrimaryExpression:
// Check for JSON operands
[
LOOKAHEAD(2, { !isPostgreSqlJsonAccess(getToken(1))
&& ((ternaryThenBranchDepth == 0 && mapKeyDepth == 0)
&& ((ternaryThenBranchDepth == 0 && mapKeyDepth == 0 && !isSliceColonAhead())
|| !":".equals(getToken(1).image)) }) (
LOOKAHEAD(2) (
token="->"
Expand Down Expand Up @@ -11983,7 +12069,7 @@ JsonExpression JsonExpression(Expression expr, List<Map.Entry<Expression, String

(
LOOKAHEAD(2, { !isPostgreSqlJsonAccess(getToken(1))
&& ((ternaryThenBranchDepth == 0 && mapKeyDepth == 0)
&& ((ternaryThenBranchDepth == 0 && mapKeyDepth == 0 && !isSliceColonAhead())
|| !":".equals(getToken(1).image)) }) (
token="->"
|
Expand Down
Loading
Loading