Skip to content
Open
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
40 changes: 40 additions & 0 deletions babel/src/test/java/org/apache/calcite/test/BabelParserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@
* limitations under the License.
*/
package org.apache.calcite.test;
import org.apache.calcite.sql.SqlCall;
import org.apache.calcite.sql.SqlDialect;
import org.apache.calcite.sql.SqlKind;
import org.apache.calcite.sql.SqlSelect;
import org.apache.calcite.sql.dialect.MysqlSqlDialect;
import org.apache.calcite.sql.dialect.PostgresqlSqlDialect;
import org.apache.calcite.sql.dialect.SparkSqlDialect;
Expand All @@ -25,6 +28,7 @@
import org.apache.calcite.sql.parser.SqlParserTest;
import org.apache.calcite.sql.parser.StringAndPos;
import org.apache.calcite.sql.parser.babel.SqlBabelParserImpl;
import org.apache.calcite.sql.validate.SqlAbstractConformance;
import org.apache.calcite.tools.Hoist;

import com.google.common.base.Throwables;
Expand Down Expand Up @@ -53,6 +57,10 @@ class BabelParserTest extends SqlParserTest {
.withConfig(c -> c.withParserFactory(SqlBabelParserImpl.FACTORY));
}

@Override protected boolean allowsDoubleColonInColonFieldAccessMode() {
return true;
}

/** Tests that the Babel parser correctly parses a CAST to INTERVAL type
* in PostgreSQL dialect. */
@Test void testCastToInterval() {
Expand Down Expand Up @@ -301,6 +309,38 @@ private void checkParseInfixCast(String sqlType) {
sql(sql).ok(expected);
}

@Test void testInfixCastBracketAccessNeedsParentheses() {
sql("select v::variant^[^1] from t")
.fails("(?s).*Encountered \"\\[\".*");
sql("select (v::variant)[1], (v::integer array)[1] from t")
.node(
customMatches("select list", node -> {
final SqlSelect select = (SqlSelect) node;
assertThat(select.getSelectList().get(0).getKind(), is(SqlKind.ITEM));
assertThat(((SqlCall) select.getSelectList().get(0)).operand(0).getKind(),
is(SqlKind.CAST));
assertThat(select.getSelectList().get(1).getKind(), is(SqlKind.ITEM));
assertThat(((SqlCall) select.getSelectList().get(1)).operand(0).getKind(),
is(SqlKind.CAST));
}));
}
@Test void testColonFieldAccessWithInfixCast() {
final SqlParserFixture f =
fixture().withConformance(new SqlAbstractConformance() {
@Override public boolean isColonFieldAccessAllowed() {
return true;
}
});
f.sql("select v:field::integer, arr[1]:field::varchar, "
+ "v:field.field2::integer, v:field[2]::integer from t")
.ok("SELECT (`V`.`FIELD`) :: INTEGER, "
+ "(`ARR`[1].`FIELD`) :: VARCHAR, "
+ "((`V`.`FIELD`).`FIELD2`) :: INTEGER, "
+ "(`V`.`FIELD`)[2] :: INTEGER\n"
+ "FROM `T`");
f.sql("select v::variant^:^field from t")
.fails("(?s).*Encountered \":.*\".*");
}
/** Tests parsing MySQL-style "<=>" equal operator. */
@Test void testParseNullSafeEqual() {
// x <=> y
Expand Down
101 changes: 72 additions & 29 deletions core/src/main/codegen/templates/Parser.jj
Original file line number Diff line number Diff line change
Expand Up @@ -3774,6 +3774,24 @@ SqlNode Expression(ExprContext exprContext) :
list = Expression2(exprContext) { return SqlParserUtil.toTree(list); }
}

void AddRegularPostfixes(List<Object> list) :
{
SqlNode ext;
}
{
(
LOOKAHEAD(2) <DOT>
ext = RowExpressionExtension() {
list.add(
new SqlParserUtil.ToTreeListItem(
SqlStdOperatorTable.DOT, getPos()));
list.add(ext);
}
|
AddBracketPostfix(list)
)*
}

void AddExpression2b(List<Object> list, ExprContext exprContext) :
{
SqlNode e;
Expand All @@ -3791,13 +3809,61 @@ void AddExpression2b(List<Object> list, ExprContext exprContext) :
e = Expression3(exprContext) {
list.add(e);
}
AddRegularPostfixes(list)
[
LOOKAHEAD(2, <COLON> SimpleIdentifier(),
{ this.conformance.isColonFieldAccessAllowed() })
<COLON>
ext = SimpleIdentifier() {
list.add(
new SqlParserUtil.ToTreeListItem(
SqlStdOperatorTable.DOT, getPos()));
list.add(ext);
}
AddRegularPostfixes(list)
|
LOOKAHEAD(2, <COLON> <LBRACKET>,
{ this.conformance.isColonFieldAccessAllowed() })
<COLON>
AddBracketAccess(list)
AddRegularPostfixes(list)
]
}

void AddBracketAccess(List<Object> list) :
{
SqlNode e;
SqlOperator itemOp;
}
{
<LBRACKET>
( <OFFSET> { itemOp = SqlLibraryOperators.OFFSET; } <LPAREN> { e = Expression(ExprContext.ACCEPT_SUB_QUERY); } <RPAREN>
| <ORDINAL> { itemOp = SqlLibraryOperators.ORDINAL; } <LPAREN> { e = Expression(ExprContext.ACCEPT_SUB_QUERY); } <RPAREN>
| <SAFE_OFFSET> { itemOp = SqlLibraryOperators.SAFE_OFFSET; } <LPAREN> { e = Expression(ExprContext.ACCEPT_SUB_QUERY); } <RPAREN>
| <SAFE_ORDINAL> { itemOp = SqlLibraryOperators.SAFE_ORDINAL; } <LPAREN> { e = Expression(ExprContext.ACCEPT_SUB_QUERY); } <RPAREN>
| { itemOp = SqlStdOperatorTable.ITEM; } e = Expression(ExprContext.ACCEPT_SUB_QUERY)
)
<RBRACKET> {
list.add(
new SqlParserUtil.ToTreeListItem(
itemOp, getPos()));
list.add(e);
}
}

void AddBracketPostfix(List<Object> list) :
{
SqlIdentifier p;
}
{
AddBracketAccess(list)
(
LOOKAHEAD(2) <DOT>
ext = RowExpressionExtension() {
p = SimpleIdentifier() {
list.add(
new SqlParserUtil.ToTreeListItem(
SqlStdOperatorTable.DOT, getPos()));
list.add(ext);
list.add(p);
}
)*
}
Expand All @@ -3823,9 +3889,7 @@ List<Object> Expression2(ExprContext exprContext) :
final List<Object> list3 = new ArrayList();
SqlNodeList nodeList;
SqlNode e;
SqlOperator itemOp;
SqlOperator op;
SqlIdentifier p;
final Span s = span();
}
{
Expand Down Expand Up @@ -3962,29 +4026,6 @@ List<Object> Expression2(ExprContext exprContext) :
list.add(new SqlParserUtil.ToTreeListItem(op, getPos()));
}
AddExpression2b(list, ExprContext.ACCEPT_SUB_QUERY)
|
<LBRACKET>
( <OFFSET> { itemOp = SqlLibraryOperators.OFFSET; } <LPAREN> { e = Expression(ExprContext.ACCEPT_SUB_QUERY); } <RPAREN>
| <ORDINAL> { itemOp = SqlLibraryOperators.ORDINAL; } <LPAREN> { e = Expression(ExprContext.ACCEPT_SUB_QUERY); } <RPAREN>
| <SAFE_OFFSET> { itemOp = SqlLibraryOperators.SAFE_OFFSET; } <LPAREN> { e = Expression(ExprContext.ACCEPT_SUB_QUERY); } <RPAREN>
| <SAFE_ORDINAL> { itemOp = SqlLibraryOperators.SAFE_ORDINAL; } <LPAREN> { e = Expression(ExprContext.ACCEPT_SUB_QUERY); } <RPAREN>
| { itemOp = SqlStdOperatorTable.ITEM; } e = Expression(ExprContext.ACCEPT_SUB_QUERY)
)
<RBRACKET> {
list.add(
new SqlParserUtil.ToTreeListItem(
itemOp, getPos()));
list.add(e);
}
(
LOOKAHEAD(2) <DOT>
p = SimpleIdentifier() {
list.add(
new SqlParserUtil.ToTreeListItem(
SqlStdOperatorTable.DOT, getPos()));
list.add(p);
}
)*
|
{
checkNonQueryExpression(exprContext);
Expand Down Expand Up @@ -7012,13 +7053,15 @@ List<SqlNode> JsonNameAndValue() :
<VALUE>
|
<COMMA> {
if (kvMode) {
if (kvMode || this.conformance.isColonFieldAccessAllowed()) {
throw SqlUtil.newContextException(getPos(), RESOURCE.illegalComma());
}
}
|
<COLON> {
if (kvMode) {
// If ':' is used for field access, JSON constructors must use
// VALUE syntax instead of ':'.
if (kvMode || this.conformance.isColonFieldAccessAllowed()) {
throw SqlUtil.newContextException(getPos(), RESOURCE.illegalColon());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,19 @@ enum SelectAliasLookup {
*/
boolean allowHyphenInUnquotedTableName();

/**
* Whether {@code :} is allowed as a field/item access operator.
*
* <p>If true, expressions such as {@code v:field}, {@code v:['x']} and
* {@code arr[1]:field} are valid. In this mode, JSON constructors must use
* {@code VALUE} syntax rather than {@code :} or comma-pair syntax.
*
* <p>Among the built-in conformance levels, false for all.
*/
default boolean isColonFieldAccessAllowed() {
return false;
}

/**
* Whether the bang-equal token != is allowed as an alternative to &lt;&gt; in
* the parser.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,10 @@ public enum SqlConformanceEnum implements SqlConformance {
}
}

@Override public boolean isColonFieldAccessAllowed() {
return false;
}

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

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

@Override public boolean isBangEqualAllowed() {
return delegate.isBangEqualAllowed();
}
Expand Down
Loading
Loading