1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.mybatis.dynamic.sql;
17
18 import static org.assertj.core.api.Assertions.assertThat;
19 import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
20 import static org.mybatis.dynamic.sql.SqlBuilder.*;
21
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.MissingResourceException;
25 import java.util.Optional;
26
27 import org.jspecify.annotations.Nullable;
28 import org.junit.jupiter.api.Test;
29 import org.mybatis.dynamic.sql.common.OrderByModel;
30 import org.mybatis.dynamic.sql.configuration.StatementConfiguration;
31 import org.mybatis.dynamic.sql.exception.InvalidSqlException;
32 import org.mybatis.dynamic.sql.insert.BatchInsertModel;
33 import org.mybatis.dynamic.sql.insert.GeneralInsertModel;
34 import org.mybatis.dynamic.sql.insert.InsertColumnListModel;
35 import org.mybatis.dynamic.sql.insert.InsertModel;
36 import org.mybatis.dynamic.sql.insert.MultiRowInsertModel;
37 import org.mybatis.dynamic.sql.render.RenderingContext;
38 import org.mybatis.dynamic.sql.render.RenderingStrategies;
39 import org.mybatis.dynamic.sql.select.GroupByModel;
40 import org.mybatis.dynamic.sql.select.PagingModel;
41 import org.mybatis.dynamic.sql.select.QueryExpressionModel;
42 import org.mybatis.dynamic.sql.select.SelectModel;
43 import org.mybatis.dynamic.sql.select.join.JoinModel;
44 import org.mybatis.dynamic.sql.select.join.JoinSpecification;
45 import org.mybatis.dynamic.sql.select.join.JoinType;
46 import org.mybatis.dynamic.sql.select.render.FetchFirstPagingModelRenderer;
47 import org.mybatis.dynamic.sql.update.UpdateModel;
48 import org.mybatis.dynamic.sql.util.InternalError;
49 import org.mybatis.dynamic.sql.util.Messages;
50
51 class InvalidSQLTest {
52
53 private static final SqlTable person = new SqlTable("person");
54 private static final SqlColumn<Integer> id = person.column("id");
55
56 @Test
57 void testInvalidGeneralInsertStatement() {
58 GeneralInsertModel.Builder builder = new GeneralInsertModel.Builder()
59 .withTable(person);
60
61 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(builder::build)
62 .withMessage(Messages.getString("ERROR.6"));
63 }
64
65 @Test
66 void testInvalidGeneralInsertStatementWhenAllOptionalsAreDropped() {
67 GeneralInsertModel model = insertInto(person)
68 .set(id).toValueWhenPresent((Integer) null)
69 .build();
70
71 assertThatExceptionOfType(InvalidSqlException.class)
72 .isThrownBy(() -> model.render(RenderingStrategies.SPRING_NAMED_PARAMETER))
73 .withMessage(Messages.getString("ERROR.9"));
74 }
75
76 @Test
77 void testInvalidInsertStatement() {
78 InsertModel.Builder<TestRow> builder = new InsertModel.Builder<TestRow>()
79 .withTable(person)
80 .withRow(new TestRow());
81
82 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(builder::build)
83 .withMessage(Messages.getString("ERROR.7"));
84 }
85
86 @Test
87 void testInvalidInsertStatementWhenAllOptionalsAreDropped() {
88 TestRow testRow = new TestRow();
89
90 InsertModel<TestRow> model = insert(testRow)
91 .into(person)
92 .map(id).toPropertyWhenPresent("id", testRow::getId)
93 .build();
94
95 assertThatExceptionOfType(InvalidSqlException.class)
96 .isThrownBy(() -> model.render(RenderingStrategies.SPRING_NAMED_PARAMETER))
97 .withMessage(Messages.getString("ERROR.10"));
98 }
99
100 @Test
101 void testInvalidMultipleInsertStatementNoRecords() {
102 MultiRowInsertModel.Builder<TestRow> builder = new MultiRowInsertModel.Builder<TestRow>()
103 .withTable(person);
104
105 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(builder::build)
106 .withMessage(Messages.getString("ERROR.20"));
107 }
108
109 @Test
110 void testInvalidMultipleInsertStatementNoMappings() {
111 List<TestRow> records = List.of(new TestRow());
112
113 MultiRowInsertModel.Builder<TestRow> builder = new MultiRowInsertModel.Builder<TestRow>()
114 .withRecords(records)
115 .withTable(person);
116
117 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(builder::build)
118 .withMessage(Messages.getString("ERROR.8"));
119 }
120
121 @Test
122 void testInvalidBatchInsertStatementNoRecords() {
123 BatchInsertModel.Builder<TestRow> builder = new BatchInsertModel.Builder<TestRow>()
124 .withTable(person);
125
126 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(builder::build)
127 .withMessage(Messages.getString("ERROR.19"));
128 }
129
130 @Test
131 void testInvalidBatchInsertStatementNoMappings() {
132 List<TestRow> records = List.of(new TestRow());
133
134 BatchInsertModel.Builder<TestRow> builder = new BatchInsertModel.Builder<TestRow>()
135 .withRecords(records)
136 .withTable(person);
137
138 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(builder::build)
139 .withMessage(Messages.getString("ERROR.5"));
140 }
141
142 @Test
143 void testInvalidEmptyInsertColumnList() {
144 List<SqlColumn<?>> list = Collections.emptyList();
145 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(() -> InsertColumnListModel.of(list))
146 .withMessage(Messages.getString("ERROR.4"));
147 }
148
149 @Test
150 void testInvalidNullInsertColumnList() {
151 assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> InsertColumnListModel.of(null));
152 }
153
154 @Test
155 void testInvalidSelectStatementWithoutQueryExpressions() {
156 SelectModel.Builder builder =
157 new SelectModel.Builder().withStatementConfiguration(new StatementConfiguration());
158
159 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(builder::build)
160 .withMessage(Messages.getString("ERROR.14"));
161 }
162
163 @Test
164 void testInvalidSelectStatementWithoutColumnList() {
165 QueryExpressionModel.Builder builder = new QueryExpressionModel.Builder()
166 .withTable(person);
167
168 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(builder::build)
169 .withMessage(Messages.getString("ERROR.13"));
170 }
171
172 @Test
173 void testInvalidSelectStatementEmptyJoinModel() {
174 List<JoinSpecification> list = Collections.emptyList();
175 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(() -> JoinModel.of(list))
176 .withMessage(Messages.getString("ERROR.15"));
177 }
178
179 @Test
180 void testInvalidSelectStatementNullJoinModel() {
181 assertThatExceptionOfType(NullPointerException.class).isThrownBy(() -> JoinModel.of(null));
182 }
183
184 @Test
185 void testInvalidSelectStatementJoinSpecification() {
186 JoinSpecification.Builder builder = new JoinSpecification.Builder()
187 .withJoinTable(person)
188 .withJoinType(JoinType.LEFT);
189
190 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(builder::build)
191 .withMessage(Messages.getString("ERROR.16"));
192 }
193 @Test
194 void testInvalidSelectStatementWithEmptyOrderByList() {
195 List<SortSpecification> list = Collections.emptyList();
196 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(() -> OrderByModel.of(list))
197 .withMessage(Messages.getString("ERROR.12"));
198 }
199
200 @Test
201 void testInvalidSelectStatementWithEmptyGroupByList() {
202 List<BasicColumn> list = Collections.emptyList();
203 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(() -> GroupByModel.of(list))
204 .withMessage(Messages.getString("ERROR.11"));
205 }
206
207 @Test
208 void testInvalidUpdateStatement() {
209 UpdateModel.Builder builder = new UpdateModel.Builder()
210 .withTable(person);
211
212 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(builder::build)
213 .withMessage(Messages.getString("ERROR.17"));
214 }
215
216 @Test
217 void testInvalidUpdateStatementWhenAllOptionalsAreDropped() {
218 UpdateModel model = update(person)
219 .set(id).equalToWhenPresent((Integer) null)
220 .build();
221
222 assertThatExceptionOfType(InvalidSqlException.class)
223 .isThrownBy(() -> model.render(RenderingStrategies.SPRING_NAMED_PARAMETER))
224 .withMessage(Messages.getString("ERROR.18"));
225 }
226
227 @Test
228 void testMissingMessage() {
229 assertThatExceptionOfType(MissingResourceException.class)
230 .isThrownBy(() -> Messages.getString("MISSING_MESSAGE"));
231 }
232
233 @Test
234 void testInvalidPagingModel() {
235 Optional<PagingModel> pagingModel = new PagingModel.Builder().withLimit(22L).build();
236
237 RenderingContext renderingContext = RenderingContext
238 .withRenderingStrategy(RenderingStrategies.MYBATIS3)
239 .withStatementConfiguration(new StatementConfiguration())
240 .build();
241
242 assertThat(pagingModel).hasValueSatisfying(pm -> {
243 FetchFirstPagingModelRenderer renderer = new FetchFirstPagingModelRenderer(renderingContext, pm);
244
245 assertThatExceptionOfType(InvalidSqlException.class)
246 .isThrownBy(renderer::render)
247 .withMessage(Messages.getInternalErrorString(InternalError.INTERNAL_ERROR_13));
248 });
249 }
250
251 @Test
252 void testInvalidValueAlias() {
253 BoundValue<Integer> foo = value(1);
254
255 assertThat(foo.alias()).isEmpty();
256 assertThatExceptionOfType(InvalidSqlException.class)
257 .isThrownBy(() -> foo.as("foo"))
258 .withMessage(Messages.getString("ERROR.38"));
259 }
260
261 @Test
262 void testInvalidDoubleForUpdate() {
263 var dsl = select(id).from(person).limit(2).forUpdate();
264 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(dsl::forUpdate)
265 .withMessage(Messages.getString("ERROR.48"));
266 }
267
268 @Test
269 void testInvalidDoubleForShare() {
270 var dsl = select(id).from(person).offset(2).forShare();
271 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(dsl::forShare)
272 .withMessage(Messages.getString("ERROR.48"));
273 }
274
275 @Test
276 void testInvalidDoubleForKeyShare() {
277 var dsl = select(id).from(person).forKeyShare();
278 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(dsl::forKeyShare)
279 .withMessage(Messages.getString("ERROR.48"));
280 }
281
282 @Test
283 void testInvalidDoubleForNoKeyUpdate() {
284 var dsl = select(id).from(person).where(id, isEqualTo(1)).forNoKeyUpdate();
285 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(dsl::forNoKeyUpdate)
286 .withMessage(Messages.getString("ERROR.48"));
287 }
288
289 @Test
290 void testInvalidDoubleForNoKeyUpdateAfterJoin() {
291 var dsl = select(id).from(person).join(person).on(id, isEqualTo(id)).skipLocked();
292 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(dsl::skipLocked)
293 .withMessage(Messages.getString("ERROR.49"));
294 }
295
296 @Test
297 void testInvalidDoubleForNoKeyUpdateAfterGroupBy() {
298 var dsl = select(id).from(person).groupBy(id).nowait();
299 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(dsl::nowait)
300 .withMessage(Messages.getString("ERROR.49"));
301 }
302
303 @Test
304 void testInvalidDoubleForNoKeyUpdateAfterHaving() {
305 var dsl = select(id).from(person).groupBy(id).having(id, isEqualTo(2)).nowait();
306 assertThatExceptionOfType(InvalidSqlException.class).isThrownBy(dsl::nowait)
307 .withMessage(Messages.getString("ERROR.49"));
308 }
309
310 static class TestRow {
311 private @Nullable Integer id;
312
313 public @Nullable Integer getId() {
314 return id;
315 }
316
317 public void setId(Integer id) {
318 this.id = id;
319 }
320 }
321 }