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 metaData = new DatabaseTableMetaData(tableName, _connection, true, super.isCaseSensitiveTableNames());
317
318 _tableMap.update(tableName, metaData);
319
320 return metaData;
321 }
322
323 public ITable getTable(String tableName) throws DataSetException
324 {
325 logger.debug("getTable(tableName={}) - start", tableName);
326
327 QualifiedTableName qualifiedTableName = new QualifiedTableName(tableName, getDefaultSchema());
328
329 initialize(qualifiedTableName.getSchema());
330
331 try
332 {
333 ITableMetaData metaData = getTableMetaData(tableName);
334
335 DatabaseConfig config = _connection.getConfig();
336 IResultSetTableFactory factory = (IResultSetTableFactory)config.getProperty(
337 DatabaseConfig.PROPERTY_RESULTSET_TABLE_FACTORY);
338 return factory.createTable(metaData, _connection);
339 }
340 catch (SQLException e)
341 {
342 throw new DataSetException(e);
343 }
344 }
345
346 private static class SchemaSet extends HashSet<String>
347 {
348 private static final long serialVersionUID = 1L;
349
350 private static final String NULL_REPLACEMENT =
351 "NULL_REPLACEMENT_HASHKEY";
352
353 private boolean isCaseSensitive;
354
355 private SchemaSet(boolean isCaseSensitive)
356 {
357 this.isCaseSensitive = isCaseSensitive;
358 }
359
360 @Override
361 public boolean contains(Object o)
362 {
363 return super.contains(normalizeSchema(o));
364 }
365
366 @Override
367 public boolean add(String e)
368 {
369 return super.add(normalizeSchema(e));
370 }
371
372 private String normalizeSchema(Object source)
373 {
374 if (source == null)
375 {
376 return NULL_REPLACEMENT;
377 } else if (!isCaseSensitive)
378 {
379 return source.toString().toUpperCase(Locale.ENGLISH);
380 }
381 return source.toString();
382 }
383 }
384
385 private static class OracleRecycleBinTableFilter implements ITableFilterSimple
386 {
387 private final DatabaseConfig _config;
388
389 public OracleRecycleBinTableFilter(DatabaseConfig config)
390 {
391 this._config = config;
392 }
393
394 public boolean accept(String tableName) throws DataSetException
395 {
396
397 if(_config.getFeature(DatabaseConfig.FEATURE_SKIP_ORACLE_RECYCLEBIN_TABLES)) {
398
399
400
401 if (tableName.startsWith("BIN$"))
402 {
403 return false;
404 }
405 }
406
407 return true;
408 }
409 }
410 }