@@ -30,4 +30,116 @@ void testColumnArrayExpression() throws JSQLParserException {
3030 assertInstanceOf (ArrayConstructor .class , column .getArrayConstructor ());
3131 }
3232
33+ @ Test
34+ void testNavigationAfterSubscript () throws JSQLParserException {
35+ // Redshift SUPER navigation: subscript then field access
36+ String sqlStr = "SELECT recommendations[0].language_id FROM recs" ;
37+ PlainSelect select = (PlainSelect ) TestUtils .assertSqlCanBeParsedAndDeparsed (sqlStr , true );
38+ SelectItem <?> selectItem = select .getSelectItem (0 );
39+
40+ RowGetExpression rowGet = selectItem .getExpression (RowGetExpression .class );
41+ assertNotNull (rowGet );
42+ assertEquals ("language_id" , rowGet .getColumnName ());
43+ Column column = assertInstanceOf (Column .class , rowGet .getExpression ());
44+ assertEquals ("recommendations" , column .getColumnName ());
45+ assertInstanceOf (ArrayConstructor .class , column .getArrayConstructor ());
46+ }
47+
48+ @ Test
49+ void testAlternatingNavigationChain () throws JSQLParserException {
50+ // field accesses and subscripts may alternate freely after the primary expression
51+ String sqlStr = "SELECT a.b[0].c[1].d FROM t" ;
52+ PlainSelect select = (PlainSelect ) TestUtils .assertSqlCanBeParsedAndDeparsed (sqlStr , true );
53+ SelectItem <?> selectItem = select .getSelectItem (0 );
54+
55+ RowGetExpression outer = selectItem .getExpression (RowGetExpression .class );
56+ assertNotNull (outer );
57+ assertEquals ("d" , outer .getColumnName ());
58+ ArrayExpression subscript = assertInstanceOf (ArrayExpression .class , outer .getExpression ());
59+ assertInstanceOf (LongValue .class , subscript .getIndexExpression ());
60+ RowGetExpression inner =
61+ assertInstanceOf (RowGetExpression .class , subscript .getObjExpression ());
62+ assertEquals ("c" , inner .getColumnName ());
63+ Column column = assertInstanceOf (Column .class , inner .getExpression ());
64+ assertEquals ("a" , column .getTable ().getName ());
65+ assertEquals ("b" , column .getColumnName ());
66+ assertInstanceOf (ArrayConstructor .class , column .getArrayConstructor ());
67+ }
68+
69+ @ Test
70+ void testNavigationFieldNames () throws JSQLParserException {
71+ // dotted continuations accept FROM/SELECT/CURRENT and quoted names,
72+ // same as ColumnIdentifier's dotted continuation (RelObjectNameExt)
73+ PlainSelect select = (PlainSelect ) TestUtils .assertSqlCanBeParsedAndDeparsed (
74+ "SELECT a[0].from FROM t" , true );
75+ RowGetExpression rowGet = select .getSelectItem (0 ).getExpression (RowGetExpression .class );
76+ assertEquals ("from" , rowGet .getColumnName ());
77+
78+ select = (PlainSelect ) TestUtils .assertSqlCanBeParsedAndDeparsed (
79+ "SELECT a[0].\" quoted field\" FROM t" , true );
80+ rowGet = select .getSelectItem (0 ).getExpression (RowGetExpression .class );
81+ assertEquals ("\" quoted field\" " , rowGet .getColumnName ());
82+ }
83+
84+ @ Test
85+ void testNavigationInClauses () throws JSQLParserException {
86+ TestUtils .assertSqlCanBeParsedAndDeparsed (
87+ "SELECT * FROM t WHERE a[0].b = 1" , true );
88+ TestUtils .assertSqlCanBeParsedAndDeparsed (
89+ "SELECT * FROM t ORDER BY a[0].b" , true );
90+ }
91+
92+ @ Test
93+ void testNavigationThenCastAndJson () throws JSQLParserException {
94+ PlainSelect select = (PlainSelect ) TestUtils .assertSqlCanBeParsedAndDeparsed (
95+ "SELECT a[0].b::text FROM t" , true );
96+ CastExpression cast = select .getSelectItem (0 ).getExpression (CastExpression .class );
97+ assertInstanceOf (RowGetExpression .class , cast .getLeftExpression ());
98+
99+ TestUtils .assertSqlCanBeParsedAndDeparsed (
100+ "SELECT a[0].b -> 'x' FROM t" , true );
101+ TestUtils .assertSqlCanBeParsedAndDeparsed (
102+ "SELECT a.b[1 : 2].c FROM t" , true );
103+ TestUtils .assertSqlCanBeParsedAndDeparsed (
104+ "SELECT a[0].b[1][2].c FROM t" , true );
105+ }
106+
107+ @ Test
108+ void testNavigationDoesNotSwallowQualifiedColumns () throws JSQLParserException {
109+ // the navigation loop must not change how plain qualified columns parse
110+ PlainSelect select = (PlainSelect ) TestUtils .assertSqlCanBeParsedAndDeparsed (
111+ "SELECT a.b FROM t" , true );
112+ Column column = select .getSelectItem (0 ).getExpression (Column .class );
113+ assertEquals ("a" , column .getTable ().getName ());
114+ assertEquals ("b" , column .getColumnName ());
115+ assertNull (column .getArrayConstructor ());
116+
117+ select = (PlainSelect ) TestUtils .assertSqlCanBeParsedAndDeparsed (
118+ "SELECT a.b.c FROM t" , true );
119+ column = select .getSelectItem (0 ).getExpression (Column .class );
120+ assertEquals ("a.b.c" , column .toString ());
121+
122+ select = (PlainSelect ) TestUtils .assertSqlCanBeParsedAndDeparsed (
123+ "SELECT a.b[0] FROM t" , true );
124+ column = select .getSelectItem (0 ).getExpression (Column .class );
125+ assertEquals ("a" , column .getTable ().getName ());
126+ assertEquals ("b" , column .getColumnName ());
127+ assertInstanceOf (ArrayConstructor .class , column .getArrayConstructor ());
128+
129+ // a bracket group after a subscript stays on the ArrayExpression slot,
130+ // not on the navigation loop
131+ select = (PlainSelect ) TestUtils .assertSqlCanBeParsedAndDeparsed (
132+ "SELECT a[0]['k'] FROM t" , true );
133+ ArrayExpression array = select .getSelectItem (0 ).getExpression (ArrayExpression .class );
134+ assertNotNull (array );
135+ column = assertInstanceOf (Column .class , array .getObjExpression ());
136+ assertInstanceOf (ArrayConstructor .class , column .getArrayConstructor ());
137+ }
138+
139+ @ Test
140+ void testMalformedNavigationRejected () {
141+ assertThrows (JSQLParserException .class ,
142+ () -> TestUtils .assertSqlCanBeParsedAndDeparsed ("SELECT a[0].* FROM t" , true ));
143+ }
144+
33145}
0 commit comments