@@ -80,6 +80,61 @@ public void templateLiterals(SessionFactoryScope scope) {
80
80
"CAST({@}.foo AS signed)" , factory );
81
81
}
82
82
83
+ @ Test
84
+ @ JiraKey ("HHH-19695" )
85
+ public void testFetchGrammarVsColumnNames (SessionFactoryScope scope ) {
86
+ SessionFactoryImplementor factory = scope .getSessionFactory ();
87
+
88
+ // Test that "first" and "next" are treated as keywords when part of FETCH grammar
89
+ assertWhereStringTemplate ( "fetch first 10 rows only" , "fetch first 10 rows only" , factory );
90
+ assertWhereStringTemplate ( "fetch next 5 rows only" , "fetch next 5 rows only" , factory );
91
+ assertWhereStringTemplate ( "select * from table fetch first 1 row only" ,
92
+ "select * from table fetch first 1 row only" , factory );
93
+
94
+ // Mixed scenarios: ensure identifiers around FETCH grammar are still qualified
95
+ assertWhereStringTemplate ( "select first_name from users fetch first 10 rows only" ,
96
+ "select {@}.first_name from users fetch first 10 rows only" , factory );
97
+ assertWhereStringTemplate ( "where fetch_count > 5 and fetch next 1 row only" ,
98
+ "where {@}.fetch_count > 5 and fetch next 1 row only" , factory );
99
+ assertWhereStringTemplate ( "select first from users fetch first 10 rows only" ,
100
+ "select {@}.first from users fetch first 10 rows only" , factory );
101
+ assertWhereStringTemplate ( "select next from users fetch next 10 rows only" ,
102
+ "select {@}.next from users fetch next 10 rows only" , factory );
103
+ }
104
+
105
+ @ Test
106
+ @ JiraKey ("HHH-19695" )
107
+ public void testFetchGrammarVariants (SessionFactoryScope scope ) {
108
+ SessionFactoryImplementor factory = scope .getSessionFactory ();
109
+ Dialect dialect = factory .getJdbcServices ().getDialect ();
110
+
111
+ // Variants of FETCH FIRST/NEXT
112
+ assertWhereStringTemplate ( "fetch first 1 row only" , "fetch first 1 row only" , factory );
113
+ assertWhereStringTemplate ( "fetch next 10 rows only" , "fetch next 10 rows only" , factory );
114
+
115
+ // Parameterized row count
116
+ assertWhereStringTemplate ( "fetch next ? rows only" , "fetch next ? rows only" , factory );
117
+
118
+ // Casing variants
119
+ assertWhereStringTemplate ( "FETCH First 10 ROWS ONLY" , "FETCH First 10 ROWS ONLY" , factory );
120
+
121
+ // Extra whitespace and newlines
122
+ assertWhereStringTemplate ( "fetch first 10 rows only" , "fetch first 10 rows only" , factory );
123
+ assertWhereStringTemplate ( "fetch\n first 3 rows only" , "fetch\n first 3 rows only" , factory );
124
+
125
+ // State reset after ONLY: trailing 'next' should be qualified
126
+ assertWhereStringTemplate ( "fetch next 1 rows only and next > 5" ,
127
+ "fetch next 1 rows only and {@}.next > 5" , factory );
128
+
129
+ // Qualified identifier should remain as-is
130
+ assertWhereStringTemplate ( "select u.first from users u fetch first 1 row only" ,
131
+ "select u.first from users u fetch first 1 row only" , factory );
132
+
133
+ // Quoted identifier should be qualified, while FETCH clause remains unqualified
134
+ assertWhereStringTemplate ( "select `first` from users fetch first 1 row only" ,
135
+ "select {@}." + dialect .quote ("`first`" ) + " from users fetch first 1 row only" , factory );
136
+ }
137
+
83
138
private static void assertWhereStringTemplate (String sql , SessionFactoryImplementor sf ) {
84
139
assertEquals ( sql ,
85
140
Template .renderWhereStringTemplate (
0 commit comments