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 java.sql.Connection;
25 import java.sql.DatabaseMetaData;
26 import java.sql.ResultSet;
27 import java.sql.SQLException;
28 import java.util.HashSet;
29 import java.util.Locale;
30 import org.dbunit.DatabaseUnitRuntimeException;
31 import org.dbunit.dataset.AbstractDataSet;
32 import org.dbunit.dataset.Column;
33 import org.dbunit.dataset.DataSetException;
34 import org.dbunit.dataset.DataSetUtils;
35 import org.dbunit.dataset.IDataSet;
36 import org.dbunit.dataset.ITable;
37 import org.dbunit.dataset.ITableIterator;
38 import org.dbunit.dataset.ITableMetaData;
39 import org.dbunit.dataset.NoSuchTableException;
40 import org.dbunit.dataset.OrderedTableNameMap;
41 import org.dbunit.dataset.filter.ITableFilterSimple;
42 import org.dbunit.util.QualifiedTableName;
43 import org.dbunit.util.SQLHelper;
44 import org.slf4j.Logger;
45 import org.slf4j.LoggerFactory;
46
47
48
49
50
51
52
53
54
55 public class DatabaseDataSet extends AbstractDataSet
56 {
57
58
59
60
61 private static final Logger logger = LoggerFactory.getLogger(DatabaseDataSet.class);
62
63 private final IDatabaseConnection _connection;
64 private OrderedTableNameMap _tableMap = null;
65 private SchemaSet _schemaSet = new SchemaSet(isCaseSensitiveTableNames());
66
67 private final ITableFilterSimple _tableFilter;
68 private final ITableFilterSimple _oracleRecycleBinTableFilter;
69
70
71
72
73
74
75 DatabaseDataSet(IDatabaseConnection connection) throws SQLException
76 {
77 this(connection, connection.getConfig().getFeature(DatabaseConfig.FEATURE_CASE_SENSITIVE_TABLE_NAMES));
78 }
79
80
81
82
83
84
85
86
87
88 public DatabaseDataSet(IDatabaseConnection connection, boolean caseSensitiveTableNames) throws SQLException
89 {
90 this(connection, caseSensitiveTableNames, null);
91 }
92
93
94
95
96
97
98
99
100
101 public DatabaseDataSet(IDatabaseConnection connection, boolean caseSensitiveTableNames, ITableFilterSimple tableFilter)
102 throws SQLException
103 {
104 super(caseSensitiveTableNames);
105 if (connection == null) {
106 throw new NullPointerException(
107 "The parameter 'connection' must not be null");
108 }
109 _connection = connection;
110 _tableFilter = tableFilter;
111 _oracleRecycleBinTableFilter = new OracleRecycleBinTableFilter(connection.getConfig());
112 }
113
114
115
116 static String getSelectStatement(String schema, ITableMetaData metaData, String escapePattern)
117 throws DataSetException
118 {
119 if (logger.isDebugEnabled())
120 {
121 logger.debug("getSelectStatement(schema={}, metaData={}, escapePattern={}) - start",
122 schema, metaData, escapePattern);
123 }
124
125 Column[] columns = metaData.getColumns();
126 Column[] primaryKeys = metaData.getPrimaryKeys();
127
128 if(columns.length==0){
129 throw new DatabaseUnitRuntimeException("At least one column is required to build a valid select statement. "+
130 "Cannot load data for " + metaData);
131 }
132
133
134 final StringBuilder sqlBuffer = new StringBuilder(128);
135 sqlBuffer.append("select ");
136 for (int i = 0; i < columns.length; i++)
137 {
138 if (i > 0)
139 {
140 sqlBuffer.append(", ");
141 }
142 String columnName = new QualifiedTableName(
143 columns[i].getColumnName(), null, escapePattern).getQualifiedName();
144 sqlBuffer.append(columnName);
145 }
146
147
148 sqlBuffer.append(" from ");
149 sqlBuffer.append(new QualifiedTableName(
150 metaData.getTableName(), schema, escapePattern).getQualifiedName());
151
152
153 for (int i = 0; i < primaryKeys.length; i++)
154 {
155 if (i == 0)
156 {
157 sqlBuffer.append(" order by ");
158 }
159 else
160 {
161 sqlBuffer.append(", ");
162 }
163 sqlBuffer.append(new QualifiedTableName(primaryKeys[i].getColumnName(), null, escapePattern).getQualifiedName());
164
165 }
166
167 return sqlBuffer.toString();
168 }
169
170
171
172
173 private void initialize(String schema) throws DataSetException
174 {
175 logger.debug("initialize() - start");
176
177 DatabaseConfig config = _connection.getConfig();
178 boolean qualifiedTableNamesActive = Boolean.TRUE == config.getProperty(DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES);
179
180 if(schema == null || !qualifiedTableNamesActive)
181 {
182
183 schema = getDefaultSchema();
184 }
185
186 if (_tableMap != null && _schemaSet.contains(schema))
187 {
188 return;
189 }
190
191 try
192 {
193 logger.debug("Initializing the data set from the database...");
194
195 Connection jdbcConnection = _connection.getConnection();
196 DatabaseMetaData databaseMetaData = jdbcConnection.getMetaData();
197
198 if(SQLHelper.isSybaseDb(jdbcConnection.getMetaData()) && !jdbcConnection.getMetaData().getUserName().equals(schema) ){
199 logger.warn("For sybase the schema name should be equal to the user name. " +
200 "Otherwise the DatabaseMetaData#getTables() method might not return any columns. " +
201 "See dbunit tracker #1628896 and http://issues.apache.org/jira/browse/TORQUE-40?page=all");
202 }
203
204 String[] tableType = (String[])config.getProperty(DatabaseConfig.PROPERTY_TABLE_TYPE);
205 IMetadataHandler metadataHandler = (IMetadataHandler) config.getProperty(DatabaseConfig.PROPERTY_METADATA_HANDLER);
206
207 ResultSet resultSet = metadataHandler.getTables(databaseMetaData, schema, tableType);
208
209 if(logger.isDebugEnabled())
210 {
211 logger.debug(SQLHelper.getDatabaseInfo(jdbcConnection.getMetaData()));
212 logger.debug("metadata resultset={}", resultSet);
213 }
214
215 try
216 {
217 if (_tableMap == null) {
218 _tableMap = super.createTableNameMap();
219 }
220 _schemaSet.add(schema);
221 while (resultSet.next())
222 {
223 String schemaName = metadataHandler.getSchema(resultSet);
224 String tableName = resultSet.getString(3);
225
226 if(_tableFilter != null && !_tableFilter.accept(tableName))
227 {
228 logger.debug("Skipping table '{}'", tableName);
229 continue;
230 }
231 if(!_oracleRecycleBinTableFilter.accept(tableName))
232 {
233 logger.debug("Skipping oracle recycle bin table '{}'", tableName);
234 continue;
235 }
236 if (schema == null && !_schemaSet.contains(schemaName)) {
237 _schemaSet.add(schemaName);
238 }
239
240 QualifiedTableName qualifiedTableName = new QualifiedTableName(tableName, schemaName);
241 tableName = qualifiedTableName.getQualifiedNameIfEnabled(config);
242
243
244 _tableMap.add(tableName, null);
245 }
246 }
247 finally
248 {
249 resultSet.close();
250 }
251 }
252 catch (SQLException e)
253 {
254 throw new DataSetException(e);
255 }
256 }
257
258 private String getDefaultSchema() {
259 return _connection.getSchema();
260 }
261
262
263
264
265 protected ITableIterator createIterator(boolean reversed)
266 throws DataSetException
267 {
268 if(logger.isDebugEnabled())
269 {
270 logger.debug("createIterator(reversed={}) - start", String.valueOf(reversed));
271 }
272
273 String[] names = getTableNames();
274 if (reversed)
275 {
276 names = DataSetUtils.reverseStringArray(names);
277 }
278
279 return new DatabaseTableIterator(names, this);
280 }
281
282
283
284
285 public String[] getTableNames() throws DataSetException
286 {
287 initialize(null);
288
289 return _tableMap.getTableNames();
290 }
291
292 public ITableMetaData getTableMetaData(String tableName) throws DataSetException
293 {
294 logger.debug("getTableMetaData(tableName={}) - start", tableName);
295
296 QualifiedTableName qualifiedTableName = new QualifiedTableName(tableName, getDefaultSchema());
297
298 initialize(qualifiedTableName.getSchema());
299
300
301 if (!_tableMap.containsTable(tableName))
302 {
303 logger.error("Table '{}' not found in tableMap={}", tableName,
304 _tableMap);
305 throw new NoSuchTableException(tableName);
306 }
307
308
309 ITableMetaData metaData = (ITableMetaData)_tableMap.get(tableName);
310 if (metaData != null)
311 {
312 return metaData;
313 }
314
315
316
317
318 String storedTableName = _tableMap.getOriginalTableName(tableName);
319 metaData = new DatabaseTableMetaData(storedTableName, _connection, true, super.isCaseSensitiveTableNames());
320
321 _tableMap.update(tableName, metaData);
322
323 return metaData;
324 }
325
326 public ITable getTable(String tableName) throws DataSetException
327 {
328 logger.debug("getTable(tableName={}) - start", tableName);
329
330 QualifiedTableName qualifiedTableName = new QualifiedTableName(tableName, getDefaultSchema());
331
332 initialize(qualifiedTableName.getSchema());
333
334 try
335 {
336 ITableMetaData metaData = getTableMetaData(tableName);
337
338 DatabaseConfig config = _connection.getConfig();
339 IResultSetTableFactory factory = (IResultSetTableFactory)config.getProperty(
340 DatabaseConfig.PROPERTY_RESULTSET_TABLE_FACTORY);
341 return factory.createTable(metaData, _connection);
342 }
343 catch (SQLException e)
344 {
345 throw new DataSetException(e);
346 }
347 }
348
349 private static class SchemaSet extends HashSet<String>
350 {
351 private static final long serialVersionUID = 1L;
352
353 private static final String NULL_REPLACEMENT =
354 "NULL_REPLACEMENT_HASHKEY";
355
356 private boolean isCaseSensitive;
357
358 private SchemaSet(boolean isCaseSensitive)
359 {
360 this.isCaseSensitive = isCaseSensitive;
361 }
362
363 @Override
364 public boolean contains(Object o)
365 {
366 return super.contains(normalizeSchema(o));
367 }
368
369 @Override
370 public boolean add(String e)
371 {
372 return super.add(normalizeSchema(e));
373 }
374
375 private String normalizeSchema(Object source)
376 {
377 if (source == null)
378 {
379 return NULL_REPLACEMENT;
380 } else if (!isCaseSensitive)
381 {
382 return source.toString().toUpperCase(Locale.ENGLISH);
383 }
384 return source.toString();
385 }
386 }
387
388 private static class OracleRecycleBinTableFilter implements ITableFilterSimple
389 {
390 private final DatabaseConfig _config;
391
392 public OracleRecycleBinTableFilter(DatabaseConfig config)
393 {
394 this._config = config;
395 }
396
397 public boolean accept(String tableName) throws DataSetException
398 {
399
400 if(_config.getFeature(DatabaseConfig.FEATURE_SKIP_ORACLE_RECYCLEBIN_TABLES)) {
401
402
403
404 if (tableName.startsWith("BIN$"))
405 {
406 return false;
407 }
408 }
409
410 return true;
411 }
412 }
413 }