Skip to content

Commit 1ec22b0

Browse files
hayssamsclaude
andcommitted
Support BigQuery array subscript OFFSET(n)
BigQuery addresses array elements positionally with OFFSET(n), ORDINAL(n), SAFE_OFFSET(n) and SAFE_ORDINAL(n). The latter three already parsed as functions; OFFSET is a reserved keyword and needed an explicit array-index rule. The accessor is kept in the AST as a Function so it deparses unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EqinwHBKuAtPEmtXr3b5P2
1 parent 6312f9e commit 1ec22b0

2 files changed

Lines changed: 48 additions & 2 deletions

File tree

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

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9459,7 +9459,7 @@ Expression ArrayExpression(Expression obj): {
94599459
Expression stopExpr = null;
94609460
} {
94619461
"["
9462-
[LOOKAHEAD(3) idxExpr = SimpleExpression()]
9462+
[LOOKAHEAD(3) idxExpr = ArrayIndexExpression()]
94639463
[
94649464
(":" { startExpr=idxExpr; idxExpr=null; })
94659465
[stopExpr = SimpleExpression()]
@@ -9468,7 +9468,7 @@ Expression ArrayExpression(Expression obj): {
94689468
{ expr = new ArrayExpression(obj, idxExpr, startExpr, stopExpr); }
94699469
(
94709470
LOOKAHEAD(2) "["
9471-
[LOOKAHEAD(3) idxExpr = SimpleExpression()]
9471+
[LOOKAHEAD(3) idxExpr = ArrayIndexExpression()]
94729472
[
94739473
(":" { startExpr=idxExpr; idxExpr=null; })
94749474
[stopExpr = SimpleExpression()]
@@ -9479,6 +9479,29 @@ Expression ArrayExpression(Expression obj): {
94799479
{ return expr; }
94809480
}
94819481

9482+
/**
9483+
* Array subscript index. BigQuery addresses array elements positionally with
9484+
* OFFSET(n), ORDINAL(n), SAFE_OFFSET(n) and SAFE_ORDINAL(n). Only OFFSET needs
9485+
* an explicit rule here, the others are plain identifiers and parse as functions.
9486+
*/
9487+
Expression ArrayIndexExpression() :
9488+
{
9489+
Token token;
9490+
Expression expr;
9491+
}
9492+
{
9493+
(
9494+
LOOKAHEAD(2) token = <K_OFFSET> "(" expr = SimpleExpression() ")"
9495+
{
9496+
return new Function()
9497+
.withName(token.image)
9498+
.withParameters(new ExpressionList<Expression>(expr));
9499+
}
9500+
|
9501+
expr = SimpleExpression() { return expr; }
9502+
)
9503+
}
9504+
94829505
Expression NavigationStep(Expression base):
94839506
{
94849507
Expression step = null;

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

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
package net.sf.jsqlparser.statement.select;
1111

1212
import net.sf.jsqlparser.JSQLParserException;
13+
import net.sf.jsqlparser.expression.ArrayExpression;
1314
import net.sf.jsqlparser.test.TestUtils;
1415
import org.junit.jupiter.api.Assertions;
1516
import org.junit.jupiter.api.Disabled;
@@ -110,4 +111,26 @@ void testTimeSeriesFunction() throws JSQLParserException {
110111
TableFunction function = select.getFromItem(TableFunction.class);
111112
Assertions.assertEquals("TABLE", function.getFunction().getExtraKeyword());
112113
}
114+
115+
@Test
116+
void testArrayOffsetAccessor() throws JSQLParserException {
117+
String sqlStr = "SELECT arr[OFFSET(0)] FROM t";
118+
PlainSelect select = (PlainSelect) TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
119+
ArrayExpression arrayExpression =
120+
(ArrayExpression) select.getSelectItem(0).getExpression();
121+
122+
Assertions.assertEquals("OFFSET(0)", arrayExpression.getIndexExpression().toString());
123+
}
124+
125+
@Test
126+
void testArraySafeOffsetAndOrdinalAccessors() throws JSQLParserException {
127+
String sqlStr = "SELECT arr[ORDINAL(1)], arr[SAFE_OFFSET(2)], arr[SAFE_ORDINAL(3)] FROM t";
128+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
129+
}
130+
131+
@Test
132+
void testOffsetClauseStillParses() throws JSQLParserException {
133+
String sqlStr = "SELECT a FROM t LIMIT 10 OFFSET 5";
134+
TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true);
135+
}
113136
}

0 commit comments

Comments
 (0)