Skip to content
Draft
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
72 changes: 44 additions & 28 deletions core/src/main/codegen/templates/Parser.jj
Original file line number Diff line number Diff line change
Expand Up @@ -5249,28 +5249,55 @@ SqlNode ArrayConstructor() :
final String p;
}
{
<ARRAY> { s = span(); }
(
<ARRAY> { s = span(); }
(
// nullary array function call: "array()" (Apache Spark)
LOOKAHEAD(2)
<LPAREN> <RPAREN> { args = SqlNodeList.EMPTY; }
(
// nullary array function call: "array()" (Apache Spark)
LOOKAHEAD(2)
<LPAREN> <RPAREN> { args = SqlNodeList.EMPTY; }
|
args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_ALL)
)
{
if (args.size() == 1 && args.get(0).isA(SqlKind.QUERY)) {
// Array query constructor, 'ARRAY (SELECT * FROM t)'
return SqlStdOperatorTable.ARRAY_QUERY.createCall(s.end(this), args.get(0));
} else {
// Spark ARRAY function, 'ARRAY(1, 2)',
// equivalent to standard 'ARRAY [1, 2]'
return SqlLibraryOperators.ARRAY.createCall(s.end(this), args.getList());
}
}
|
args = ParenthesizedQueryOrCommaList(ExprContext.ACCEPT_ALL)
)
{
if (args.size() == 1 && args.get(0).isA(SqlKind.QUERY)) {
// Array query constructor, 'ARRAY (SELECT * FROM t)'
return SqlStdOperatorTable.ARRAY_QUERY.createCall(s.end(this), args.get(0));
} else {
// Spark ARRAY function, 'ARRAY(1, 2)',
// equivalent to standard 'ARRAY [1, 2]'
return SqlLibraryOperators.ARRAY.createCall(s.end(this), args.getList());
// by enumeration "ARRAY[e0, e1, ..., eN]"
<LBRACKET> // TODO: do trigraph as well ??( ??)
(
args = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY)
|
{ args = SqlNodeList.EMPTY; }
)
<RBRACKET>
{
return SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR.createCall(
s.end(this), args.getList());
}
}
<#if (parser.includeParsingStringLiteralAsArrayLiteral!default.parser.includeParsingStringLiteralAsArrayLiteral) >
|
p = SimpleStringLiteral() {
try {
return SqlParserUtil.parseArrayLiteral(p);
} catch (SqlParseException ex) {
throw SqlUtil.newContextException(getPos(),
RESOURCE.illegalArrayExpression(p));
}
}
</#if>
)
|
// by enumeration "ARRAY[e0, e1, ..., eN]"
<LBRACKET> // TODO: do trigraph as well ??( ??)
// BigQuery bare bracket array literal "[e0, e1, ..., eN]"
LOOKAHEAD({ this.conformance.allowBareBracketArrayLiteral() })
<LBRACKET> { s = span(); }
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can handle the empty argument list similar to the spark array constructor, and then the validator won't reject [] anymore. That is rather ugly, it's true, maybe we should fix the validator instead.

(
args = ExpressionCommaList(s, ExprContext.ACCEPT_SUB_QUERY)
|
Expand All @@ -5281,17 +5308,6 @@ SqlNode ArrayConstructor() :
return SqlStdOperatorTable.ARRAY_VALUE_CONSTRUCTOR.createCall(
s.end(this), args.getList());
}
<#if (parser.includeParsingStringLiteralAsArrayLiteral!default.parser.includeParsingStringLiteralAsArrayLiteral) >
|
p = SimpleStringLiteral() {
try {
return SqlParserUtil.parseArrayLiteral(p);
} catch (SqlParseException ex) {
throw SqlUtil.newContextException(getPos(),
RESOURCE.illegalArrayExpression(p));
}
}
</#if>
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,10 @@ public abstract class SqlAbstractConformance implements SqlConformance {
return SqlConformanceEnum.DEFAULT.allowExtendedTrim();
}

@Override public boolean allowBareBracketArrayLiteral() {
return SqlConformanceEnum.DEFAULT.allowBareBracketArrayLiteral();
}

@Override public boolean allowPluralTimeUnits() {
return SqlConformanceEnum.DEFAULT.allowPluralTimeUnits();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,18 @@ default boolean isColonFieldAccessAllowed() {
*/
boolean allowExtendedTrim();

/**
* Whether array literals may omit the {@code ARRAY} keyword, using
* bare square brackets, as in BigQuery, e.g. {@code SELECT [1, 2, 3]}.
*
* <p>Among the built-in conformance levels, true in
* {@link SqlConformanceEnum#BABEL},
* {@link SqlConformanceEnum#BIG_QUERY},
* {@link SqlConformanceEnum#LENIENT};
* false otherwise.
*/
boolean allowBareBracketArrayLiteral();

/**
* Whether interval literals should allow plural time units
* such as "YEARS" and "DAYS" in interval literals.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,17 @@ public enum SqlConformanceEnum implements SqlConformance {
}
}

@Override public boolean allowBareBracketArrayLiteral() {
switch (this) {
case BABEL:
case BIG_QUERY:
case LENIENT:
return true;
default:
return false;
}
}

@Override public boolean allowPluralTimeUnits() {
switch (this) {
case BABEL:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ protected SqlDelegatingConformance(SqlConformance delegate) {
return delegate.allowExtendedTrim();
}

@Override public boolean allowBareBracketArrayLiteral() {
return delegate.allowBareBracketArrayLiteral();
}

@Override public boolean allowPluralTimeUnits() {
return delegate.allowPluralTimeUnits();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6511,6 +6511,37 @@
expr("array[^select^ 1]").fails("(?s)Encountered \"select\".*");
}

/** Test case for <a href="https://issues.apache.org/jira/browse/CALCITE-7566">[CALCITE-7566]
* Support BigQuery-style bare bracket array literals</a>. */
@Test void testBareBracketArrayLiteral() {
final SqlParserFixture bq =
fixture().withConformance(SqlConformanceEnum.BIG_QUERY).expression();
bq.sql("[1, 2, 3]").ok("(ARRAY[1, 2, 3])");

Check failure on line 6519 in testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "(ARRAY[1, 2, 3])" 3 times.

See more on https://sonarcloud.io/project/issues?id=apache_calcite&issues=AZ58z8p8feNRQCeuDWKM&open=AZ58z8p8feNRQCeuDWKM&pullRequest=4975

Check failure on line 6519 in testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Define a constant instead of duplicating this literal "[1, 2, 3]" 3 times.

See more on https://sonarcloud.io/project/issues?id=apache_calcite&issues=AZ58z8p8feNRQCeuDWKL&open=AZ58z8p8feNRQCeuDWKL&pullRequest=4975
// parser allows empty array; validator will reject it
bq.sql("[]").ok("(ARRAY[])");
// nested bare bracket literals
bq.sql("[[1, 2], [3, 4]]")
.ok("(ARRAY[(ARRAY[1, 2]), (ARRAY[3, 4])])");
// subscript directly on a bare bracket literal
bq.sql("[10, 20, 30][1]").ok("(ARRAY[10, 20, 30])[1]");
// SELECT context
fixture().withConformance(SqlConformanceEnum.BIG_QUERY)
.sql("select [1, 2, 3]")
.ok("SELECT (ARRAY[1, 2, 3])");

// BABEL and LENIENT also accept the bare bracket syntax
fixture().withConformance(SqlConformanceEnum.BABEL).expression()
.sql("[1, 2, 3]").ok("(ARRAY[1, 2, 3])");
fixture().withConformance(SqlConformanceEnum.LENIENT).expression()
.sql("[1, 2, 3]").ok("(ARRAY[1, 2, 3])");

// Strict conformance levels still reject the bare bracket syntax
expr("^[^1, 2, 3]").fails("(?s).*Encountered \"\\[\".*");
fixture().withConformance(SqlConformanceEnum.STRICT_2003).expression()
.sql("^[^1, 2, 3]")
.fails("(?s).*Encountered \"\\[\".*");
}

@Test void testArrayFunction() {
expr("array()").ok("ARRAY()");
expr("array(1)").ok("ARRAY(1)");
Expand Down
Loading