1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package org.dbunit.database;
23
24 import static org.assertj.core.api.Assertions.assertThat;
25 import static org.junit.jupiter.api.Assertions.assertThrows;
26 import static org.junit.jupiter.api.Assertions.assertTrue;
27
28 import java.sql.DatabaseMetaData;
29 import java.util.ArrayList;
30 import java.util.List;
31 import java.util.Locale;
32
33 import org.junit.jupiter.api.Assumptions;
34
35 import org.dbunit.AbstractDatabaseIT;
36 import org.dbunit.DatabaseEnvironment;
37 import org.dbunit.DdlExecutor;
38 import org.dbunit.TestFeature;
39 import org.dbunit.TurkishDefaultLocale;
40 import org.dbunit.dataset.Column;
41 import org.dbunit.dataset.Columns;
42 import org.dbunit.dataset.IDataSet;
43 import org.dbunit.dataset.ITable;
44 import org.dbunit.dataset.ITableMetaData;
45 import org.dbunit.dataset.NoSuchTableException;
46 import org.dbunit.dataset.datatype.DataType;
47 import org.dbunit.dataset.datatype.DataTypeException;
48 import org.dbunit.dataset.datatype.DefaultDataTypeFactory;
49 import org.dbunit.dataset.datatype.IDataTypeFactory;
50 import org.dbunit.dataset.filter.IColumnFilter;
51 import org.dbunit.testutil.TestUtils;
52 import org.junit.jupiter.api.Test;
53
54
55
56
57
58
59 class DatabaseTableMetaDataIT extends AbstractDatabaseIT
60 {
61
62 public static final String TEST_TABLE = "TEST_TABLE";
63
64
65
66
67
68
69
70
71 private void refreshConnection() throws Exception
72 {
73 _connection.close();
74 _connection = getDatabaseTester().getConnection();
75 setUpDatabaseConfig(_connection.getConfig());
76 }
77
78 protected IDataSet createDataSet() throws Exception
79 {
80 return _connection.createDataSet();
81 }
82
83 @Override
84 protected String convertString(final String str) throws Exception
85 {
86 return DatabaseEnvironment.getInstance().convertString(str);
87 }
88
89 @Test
90 void testGetPrimaryKeys_withPkTable_returnsAllPrimaryKeyColumns() throws Exception
91 {
92 final String tableName = "PK_TABLE";
93
94 final String[] expected = {"PK0", "PK1", "PK2"};
95
96 final ITableMetaData metaData =
97 createDataSet().getTableMetaData(tableName);
98 final Column[] columns = metaData.getPrimaryKeys();
99 assertThat(columns).as("pl count").hasSize(expected.length);
100
101 for (int i = 0; i < columns.length; i++)
102 {
103 final Column column = columns[i];
104 assertThat(column.getColumnName()).as("name")
105 .isEqualTo(convertString(expected[i]));
106 }
107 }
108
109 @Test
110 void testGetPrimaryKeys_withTableHavingNoPk_returnsEmptyArray() throws Exception
111 {
112 final String tableName = TEST_TABLE;
113
114 final ITableMetaData metaData =
115 createDataSet().getTableMetaData(tableName);
116 final Column[] columns = metaData.getPrimaryKeys();
117 assertThat(columns).as("pk count").isEmpty();
118 }
119
120 @Test
121 void testGetPrimaryKeys_withFilterYieldingNoColumns_fallsBackToDatabaseDeclaredPrimaryKeys()
122 throws Exception
123 {
124 final String tableName = "PK_TABLE";
125 final String[] expected = {"PK0", "PK1", "PK2"};
126 final IColumnFilter noMatchFilter = (filterTableName, column) -> false;
127
128 _connection.getConfig().setProperty(
129 DatabaseConfig.PROPERTY_PRIMARY_KEY_FILTER, noMatchFilter);
130 try
131 {
132 final ITableMetaData metaData =
133 createDataSet().getTableMetaData(tableName);
134 final Column[] columns = metaData.getPrimaryKeys();
135
136 assertThat(columns).as("pk count").hasSize(expected.length);
137 for (int i = 0; i < columns.length; i++)
138 {
139 assertThat(columns[i].getColumnName()).as("name")
140 .isEqualTo(convertString(expected[i]));
141 }
142 }
143 finally
144 {
145 _connection.getConfig().setProperty(
146 DatabaseConfig.PROPERTY_PRIMARY_KEY_FILTER, null);
147 }
148 }
149
150 @Test
151 void testCreation_withUnknownTable_throwsNoSuchTableException() throws Exception
152 {
153 final String tableName = "UNKNOWN_TABLE";
154 final IDatabaseConnection connection = getConnection();
155 final String schema = connection.getSchema();
156 final NoSuchTableException expected = assertThrows(
157 NoSuchTableException.class,
158 () -> new DatabaseTableMetaData(tableName, getConnection()),
159 "Should not be able to create a DatabaseTableMetaData for an unknown table");
160
161 final String msg =
162 "Did not find table '" + convertString("UNKNOWN_TABLE")
163 + "' in schema '" + schema + "'";
164 assertThat(expected).hasMessage(msg);
165 }
166
167 @Test
168 void testGetColumns_withUnknownTableAndNoValidation_returnsEmptyColumnArray() throws Exception
169 {
170
171
172 final String tableName = "UNKNOWN_TABLE";
173 final boolean validate = false;
174
175 final ITableMetaData metaData =
176 new DatabaseTableMetaData(tableName, getConnection(), validate);
177
178 final Column[] columns = metaData.getColumns();
179 assertThat(columns).isEmpty();
180 }
181
182 @Test
183 void testGetColumns_withPkTable_returnsCorrectNullabilityForEachColumn() throws Exception
184 {
185 final String tableName = "PK_TABLE";
186 final String[] notNullable = {"PK0", "PK1", "PK2"};
187 final String[] nullable = {"NORMAL0", "NORMAL1"};
188
189 final ITableMetaData metaData =
190 createDataSet().getTableMetaData(tableName);
191 final Column[] columns = metaData.getColumns();
192
193 assertThat(columns).as("column count")
194 .hasSize(nullable.length + notNullable.length);
195
196
197 for (int i = 0; i < notNullable.length; i++)
198 {
199 final Column column = Columns.getColumn(notNullable[i], columns);
200 assertThat(column.getNullable()).as(notNullable[i])
201 .isEqualTo(Column.NO_NULLS);
202 }
203
204
205 for (int i = 0; i < nullable.length; i++)
206 {
207 final Column column = Columns.getColumn(nullable[i], columns);
208 assertThat(column.getNullable()).as(nullable[i])
209 .isEqualTo(Column.NULLABLE);
210 }
211 }
212
213 @Test
214 void testGetColumns_whenDataTypeFactoryReturnsUnknown_returnsEmptyColumnArray() throws Exception
215 {
216 final IDataTypeFactory dataTypeFactory = new DefaultDataTypeFactory()
217 {
218 @Override
219 public DataType createDataType(final int sqlType,
220 final String sqlTypeName, final String tableName,
221 final String columnName) throws DataTypeException
222 {
223 return DataType.UNKNOWN;
224 }
225 };
226 this._connection.getConfig().setProperty(
227 DatabaseConfig.PROPERTY_DATATYPE_FACTORY, dataTypeFactory);
228
229 final String tableName = "EMPTY_MULTITYPE_TABLE";
230 final ITableMetaData metaData =
231 createDataSet().getTableMetaData(tableName);
232 final Column[] columns = metaData.getColumns();
233
234 assertThat(columns).as("Should be an empty column array").isEmpty();
235 }
236
237 @Test
238 void testGetColumns_withMultitypeTable_returnsCorrectDataTypeForEachColumn() throws Exception
239 {
240 final String tableName = "EMPTY_MULTITYPE_TABLE";
241
242 final List<String> expectedNames = new ArrayList<>();
243 expectedNames.add("VARCHAR_COL");
244 expectedNames.add("NUMERIC_COL");
245 expectedNames.add("TIMESTAMP_COL");
246
247 final List<DataType> expectedTypes = new ArrayList<>();
248 expectedTypes.add(DataType.VARCHAR);
249 expectedTypes.add(DataType.NUMERIC);
250 expectedTypes.add(DataType.TIMESTAMP);
251
252 final DatabaseEnvironment environment =
253 DatabaseEnvironment.getInstance();
254 if (environment.support(TestFeature.VARBINARY))
255 {
256 expectedNames.add("VARBINARY_COL");
257 expectedTypes.add(DataType.VARBINARY);
258 }
259
260
261 assertThat(expectedNames).as("expected columns")
262 .hasSize(expectedTypes.size());
263
264 final ITableMetaData metaData =
265 createDataSet().getTableMetaData(tableName);
266 final Column[] columns = metaData.getColumns();
267 assertThat(columns).as("column count").hasSize(4);
268
269 for (int i = 0; i < expectedNames.size(); i++)
270 {
271 final Column column = columns[i];
272 assertThat(column.getColumnName()).as("name")
273 .isEqualTo(convertString(expectedNames.get(i)));
274 if (expectedTypes.get(i).equals(DataType.NUMERIC))
275 {
276
277
278 assertThat(column)
279 .as("Expected numeric datatype, got ["
280 + column.getDataType() + "]")
281 .satisfiesAnyOf(
282 dataType -> assertThat(dataType.getDataType())
283 .isEqualTo(DataType.NUMERIC),
284 dataType -> assertThat(dataType.getDataType())
285 .isEqualTo(DataType.DECIMAL));
286
287 } else if (expectedTypes.get(i).equals(DataType.TIMESTAMP)
288 && column.getDataType().equals(DataType.DATE))
289 {
290
291
292
293 assertTrue(true);
294 } else if (expectedTypes.get(i).equals(DataType.VARBINARY)
295 && column.getDataType().equals(DataType.VARCHAR))
296 {
297
298
299
300 assertTrue(true);
301 } else
302 {
303 assertThat(column.getDataType()).as("datatype")
304 .isEqualTo(expectedTypes.get(i));
305
306 }
307 }
308 }
309
310
311
312
313
314
315
316
317 @Test
318 @TurkishDefaultLocale
319 void testGetTable_withTurkishLocaleActive_findsTableIgnoringLocaleSpecificUpperCase() throws Exception
320 {
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335 final String tableName = "EMPTY_MULTITYPE_TABLE";
336
337 final IDataSet dataSet = this._connection.createDataSet();
338 final ITable table = dataSet.getTable(tableName);
339
340
341 assertThat(table).as("Table '" + tableName + "' was not found")
342 .isNotNull();
343 }
344
345
346
347
348
349
350
351 @Test
352 void testGetColumns_withPatternLikeTableName_doesNotInterpretUnderscoreAsPattern() throws Exception
353 {
354 DdlExecutor.dropTables(_connection.getConnection(),
355 "PATTERN_LIKE_TABLE_XX", "PATTERN_LIKE_TABLE_X_");
356 DdlExecutor.executeDdlFile(
357 TestUtils.getFile("sql/hypersonic_dataset_pattern_test.sql"),
358 _connection.getConnection(), false);
359 try
360 {
361 final String tableName = "PATTERN_LIKE_TABLE_X_";
362 final String[] columnNames = {"VARCHAR_COL_XUNDERSCORE"};
363
364 final ITableMetaData metaData =
365 _connection.createDataSet().getTableMetaData(tableName);
366 final Column[] columns = metaData.getColumns();
367 assertThat(columns).as("column count").hasSize(columnNames.length);
368
369 for (int i = 0; i < columnNames.length; i++)
370 {
371 final Column column =
372 Columns.getColumn(columnNames[i], columns);
373 assertThat(column.getColumnName()).as(columnNames[i])
374 .isEqualToIgnoringCase(columnNames[i]);
375 }
376 }
377 finally
378 {
379 DdlExecutor.dropTables(_connection.getConnection(),
380 "PATTERN_LIKE_TABLE_XX", "PATTERN_LIKE_TABLE_X_");
381 refreshConnection();
382 }
383 }
384
385 @Test
386 void testCreation_withCaseSensitiveEnabled_onlyFindsMixedCaseTable() throws Exception
387 {
388 final java.sql.DatabaseMetaData dbMeta =
389 _connection.getConnection().getMetaData();
390 Assumptions.assumeTrue(
391 dbMeta.supportsMixedCaseQuotedIdentifiers(),
392 "Skip: database does not treat quoted identifiers as case-sensitive.");
393 Assumptions.assumeTrue(
394 "\"".equals(dbMeta.getIdentifierQuoteString()),
395 "Skip: database does not use ANSI double-quote identifier syntax.");
396 Assumptions.assumeFalse(
397 dbMeta.storesLowerCaseIdentifiers(),
398 "Skip: database stores unquoted identifiers in lowercase.");
399 DdlExecutor.dropTables(_connection.getConnection(),
400 "UPPER_CASE_TABLE");
401 try
402 {
403 DdlExecutor.executeSql(_connection.getConnection(),
404 "DROP TABLE \"MixedCaseTable\"");
405 }
406 catch (final Exception ignored)
407 {
408
409 }
410 DdlExecutor.executeDdlFile(
411 TestUtils.getFile("sql/hypersonic_case_sensitive_test.sql"),
412 _connection.getConnection(), false);
413 try
414 {
415 final String tableName = "MixedCaseTable";
416 final String tableNameWrongCase = "MIXEDCASETABLE";
417 final boolean validate = true;
418 final boolean caseSensitive = true;
419
420
421
422 final String schemaName = _connection.getSchema();
423 try (java.sql.ResultSet wrongCaseRs = _connection.getConnection().getMetaData()
424 .getTables(null, schemaName, tableNameWrongCase, null))
425 {
426 Assumptions.assumeFalse(wrongCaseRs.next(),
427 "Skip: database finds 'MixedCaseTable' via 'MIXEDCASETABLE' (case-insensitive identifiers).");
428 }
429
430 final ITableMetaData metaData = new DatabaseTableMetaData(tableName,
431 _connection, validate, caseSensitive);
432 final Column[] columns = metaData.getColumns();
433 assertThat(columns).hasSize(1);
434 assertThat(columns[0].getColumnName()).isEqualTo("COL1");
435
436
437 final NoSuchTableException expected =
438 assertThrows(NoSuchTableException.class, () -> {
439 new DatabaseTableMetaData(tableNameWrongCase,
440 _connection, validate, caseSensitive);
441 }, "Should not be able to create DatabaseTableMetaData with non-existing table name "
442 + tableNameWrongCase + ". Created ");
443 assertThat(expected.getMessage().indexOf(tableNameWrongCase))
444 .isNotNegative();
445 }
446 finally
447 {
448 DdlExecutor.dropTables(_connection.getConnection(),
449 "UPPER_CASE_TABLE");
450 try
451 {
452 DdlExecutor.executeSql(_connection.getConnection(),
453 "DROP TABLE \"MixedCaseTable\"");
454 }
455 catch (final Exception ignored)
456 {
457
458 }
459 refreshConnection();
460 }
461 }
462
463
464
465
466
467
468
469
470 @Test
471 void testGetTableName_withFullyQualifiedSchemaTableName_returnsSchemaPrefixedName() throws Exception
472 {
473 final DatabaseEnvironment environment =
474 DatabaseEnvironment.getInstance();
475 final String schema = environment.getProfile().getSchema();
476
477 assertThat(schema)
478 .as("Precondition: db environment 'schema' must not be null")
479 .isNotNull();
480
481
482 final DatabaseTableMetaData metaData = new DatabaseTableMetaData(
483 schema + "." + TEST_TABLE, _connection);
484 assertThat(metaData.getTableName())
485 .isEqualTo(schema + "." + convertString(TEST_TABLE));
486 }
487
488 @Test
489 void testGetTableName_whenDatabaseStoresUpperCase_returnsUpperCasedTableName() throws Exception
490 {
491 final IDatabaseConnection connection = getConnection();
492 final DatabaseMetaData metaData =
493 connection.getConnection().getMetaData();
494 if (metaData.storesUpperCaseIdentifiers())
495 {
496 final DatabaseTableMetaData dbTableMetaData =
497 new DatabaseTableMetaData(
498 TEST_TABLE.toLowerCase(Locale.ENGLISH),
499 _connection);
500
501 assertThat(dbTableMetaData.getTableName())
502 .isEqualTo(TEST_TABLE.toUpperCase(Locale.ENGLISH));
503 } else
504 {
505
506 assertTrue(true);
507 }
508 }
509
510 @Test
511 void testGetTableName_whenDatabaseStoresLowerCase_returnsLowerCasedTableName() throws Exception
512 {
513 final IDatabaseConnection connection = getConnection();
514 final DatabaseMetaData metaData =
515 connection.getConnection().getMetaData();
516 if (metaData.storesLowerCaseIdentifiers())
517 {
518 final DatabaseTableMetaData dbTableMetaData =
519 new DatabaseTableMetaData(
520 TEST_TABLE.toUpperCase(Locale.ENGLISH),
521 _connection);
522
523 assertThat(dbTableMetaData.getTableName())
524 .isEqualTo(TEST_TABLE.toLowerCase(Locale.ENGLISH));
525 } else
526 {
527
528 assertTrue(true);
529 }
530 }
531 }