diff --git a/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java b/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java index f458a174da6c..93cfc5910de1 100644 --- a/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java +++ b/babel/src/test/java/org/apache/calcite/test/BabelParserTest.java @@ -301,6 +301,47 @@ private void checkParseInfixCast(String sqlType) { sql(sql).ok(expected); } + @Test void testColonFieldAccessWithInfixCast() { + // Brackets after :: bind to the type, not as subscripts on the cast result. + sql("select v::varchar array[1] from t") + .ok("SELECT `V` :: VARCHAR ARRAY[1]\nFROM `T`"); + sql("select (v::varchar array)[1] from t") + .ok("SELECT `V` :: VARCHAR ARRAY[1]\nFROM `T`"); + sql("select v::integer array[1] from t") + .ok("SELECT `V` :: INTEGER ARRAY[1]\nFROM `T`"); + sql("select (v::integer array)[1] from t") + .ok("SELECT `V` :: INTEGER ARRAY[1]\nFROM `T`"); + sql("select v::varchar array[1][2] from t") + .ok("SELECT `V` :: VARCHAR ARRAY[1][2]\nFROM `T`"); + sql("select (v + 1)::integer array[1] from t") + .ok("SELECT (`V` + 1) :: INTEGER ARRAY[1]\nFROM `T`"); + + // These cases make the postfix contrast explicit for the baseline parser + // behavior on this branch; the core bracket path is covered separately in + // SqlParserTest. + sql("select v.field[1].field2 from t") + .ok("SELECT (`V`.`FIELD`[1].`FIELD2`)\nFROM `T`"); + sql("select arr.field.func() from t") + .ok("SELECT `ARR`.`FIELD`.`FUNC`()\nFROM `T`"); + + // With ::, [n].field is parsed as part of the target type unless grouped. + sql("select v::varchar array[1].field from t") + .ok("SELECT `V` :: (VARCHAR ARRAY[1].`FIELD`)\nFROM `T`"); + sql("select (v::varchar array)[1].field from t") + .ok("SELECT (`V` :: VARCHAR ARRAY[1].`FIELD`)\nFROM `T`"); + + sql("select v.field::integer,\n" + + " arr[1].field::varchar,\n" + + " v.field.field2::integer,\n" + + " v.field[2]::integer\n" + + "from t") + .ok("SELECT `V`.`FIELD` :: INTEGER," + + " (`ARR`[1].`FIELD`) :: VARCHAR," + + " `V`.`FIELD`.`FIELD2` :: INTEGER," + + " `V`.`FIELD`[2] :: INTEGER\n" + + "FROM `T`"); + } + /** Tests parsing MySQL-style "<=>" equal operator. */ @Test void testParseNullSafeEqual() { // x <=> y diff --git a/testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java b/testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java index cfea5b6ce0e9..547b1ec4b477 100644 --- a/testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java +++ b/testkit/src/main/java/org/apache/calcite/sql/parser/SqlParserTest.java @@ -7836,6 +7836,34 @@ private static Consumer> checkWarnings( + "FROM `TBL`"); } + @Test void testBracketPostfixDoesNotAllowMemberFunction() { + // Bracket postfixes allow ".field" but not suffix member calls like ".func()". + sql("SELECT arr[1].func^(^) FROM tbl") + .fails(ANY); + } + + @Test void testDotPostfixAllowsMemberFunction() { + // Plain dot chains still support suffix member functions. + sql("SELECT arr.field.func() FROM tbl") + .ok("SELECT `ARR`.`FIELD`.`FUNC`()\n" + + "FROM `TBL`"); + } + + @Test void testBracketPostfixPreservesExistingAccessForms() { + // Regression marker for the bracket-access path that was moved out of the + // infix loop: simple item access, item-then-field, and nested item access + // should all keep parsing the same way. + sql("SELECT arr[1] FROM tbl") + .ok("SELECT `ARR`[1]\n" + + "FROM `TBL`"); + sql("SELECT arr[1].field FROM tbl") + .ok("SELECT (`ARR`[1].`FIELD`)\n" + + "FROM `TBL`"); + sql("SELECT arr[1][2] FROM tbl") + .ok("SELECT `ARR`[1][2]\n" + + "FROM `TBL`"); + } + @Test void testUnicodeLiteral() { // Note that here we are constructing a SQL statement which directly // contains Unicode characters (not SQL Unicode escape sequences). The