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.util;
23
24 import java.io.PrintStream;
25 import java.sql.Connection;
26 import java.sql.DatabaseMetaData;
27 import java.sql.ResultSet;
28 import java.sql.SQLException;
29 import java.sql.Statement;
30 import java.util.Locale;
31
32 import org.dbunit.DatabaseUnitRuntimeException;
33 import org.dbunit.database.IMetadataHandler;
34 import org.dbunit.dataset.Column;
35 import org.dbunit.dataset.datatype.DataType;
36 import org.dbunit.dataset.datatype.DataTypeException;
37 import org.dbunit.dataset.datatype.IDataTypeFactory;
38 import org.slf4j.Logger;
39 import org.slf4j.LoggerFactory;
40
41
42
43
44
45
46
47
48
49
50 public class SQLHelper {
51
52
53
54
55 public static final String DB_PRODUCT_SYBASE = "Sybase";
56
57
58
59
60 private static final Logger logger = LoggerFactory.getLogger(SQLHelper.class);
61
62
63 private SQLHelper() {}
64
65
66
67
68
69
70
71
72 public static String getPrimaryKeyColumn( Connection conn, String table ) throws SQLException {
73 logger.debug("getPrimaryKeyColumn(conn={}, table={}) - start", conn, table);
74
75 DatabaseMetaData metadata = conn.getMetaData();
76 ResultSet rs = metadata.getPrimaryKeys( null, null, table );
77 rs.next();
78 String pkColumn = rs.getString(4);
79 return pkColumn;
80 }
81
82
83
84
85
86
87
88 public static void close(ResultSet rs, Statement stmt) throws SQLException {
89 logger.debug("close(rs={}, stmt={}) - start", rs, stmt);
90
91 try {
92 SQLHelper.close(rs);
93 } finally {
94 SQLHelper.close( stmt );
95 }
96 }
97
98
99
100
101
102
103 public static void close(Statement stmt) throws SQLException {
104 logger.debug("close(stmt={}) - start", stmt);
105
106 if ( stmt != null ) {
107 stmt.close();
108 }
109 }
110
111
112
113
114
115
116 public static void close(ResultSet resultSet) throws SQLException {
117 logger.debug("close(resultSet={}) - start", resultSet);
118
119 if(resultSet != null) {
120 resultSet.close();
121 }
122 }
123
124
125
126
127
128
129
130
131
132 public static boolean schemaExists(Connection connection, String schema)
133 throws SQLException
134 {
135 logger.trace("schemaExists(connection={}, schema={}) - start", connection, schema);
136
137 if(schema == null)
138 {
139 throw new NullPointerException("The parameter 'schema' must not be null");
140 }
141
142 DatabaseMetaData metaData = connection.getMetaData();
143 ResultSet rs = metaData.getSchemas();
144 try
145 {
146 while(rs.next())
147 {
148 String foundSchema = rs.getString("TABLE_SCHEM");
149 if(foundSchema.equals(schema))
150 {
151 return true;
152 }
153 }
154
155
156 if(catalogExists(connection, schema))
157 {
158 logger.debug("Found catalog with name {}. Returning true because DB is probably on MySQL", schema);
159 return true;
160 }
161
162 return false;
163 }
164 finally
165 {
166 rs.close();
167 }
168 }
169
170
171
172
173
174
175
176
177
178 private static boolean catalogExists(Connection connection, String catalog) throws SQLException
179 {
180 logger.trace("catalogExists(connection={}, catalog={}) - start", connection, catalog);
181
182 if(catalog == null)
183 {
184 throw new NullPointerException("The parameter 'catalog' must not be null");
185 }
186
187 DatabaseMetaData metaData = connection.getMetaData();
188 ResultSet rs = metaData.getCatalogs();
189 try
190 {
191 while(rs.next())
192 {
193 String foundCatalog = rs.getString("TABLE_CAT");
194 if(foundCatalog.equals(catalog))
195 {
196 return true;
197 }
198 }
199 return false;
200 }
201 finally
202 {
203 rs.close();
204 }
205
206 }
207
208
209
210
211
212
213
214
215
216
217
218
219
220 public static boolean tableExists(DatabaseMetaData metaData, String schema,
221 String tableName)
222 throws SQLException
223 {
224 ResultSet tableRs = metaData.getTables(null, schema, tableName, null);
225 try
226 {
227 return tableRs.next();
228 }
229 finally
230 {
231 SQLHelper.close(tableRs);
232 }
233 }
234
235
236
237
238
239
240
241 public static void printAllTables(DatabaseMetaData metaData, PrintStream outputStream) throws SQLException
242 {
243 ResultSet rs = metaData.getTables(null, null, null, null);
244 try
245 {
246 while (rs.next())
247 {
248 String catalog = rs.getString("TABLE_CAT");
249 String schema = rs.getString("TABLE_SCHEM");
250 String table = rs.getString("TABLE_NAME");
251 final StringBuilder tableInfo = new StringBuilder();
252 if(catalog!=null) tableInfo.append(catalog).append(".");
253 if(schema!=null) tableInfo.append(schema).append(".");
254 tableInfo.append(table);
255
256 outputStream.println(tableInfo);
257 }
258 outputStream.flush();
259 }
260 finally
261 {
262 SQLHelper.close(rs);
263 }
264
265 }
266
267
268
269
270
271
272 public static String getDatabaseInfo(DatabaseMetaData metaData)
273 {
274 final StringBuilder sb = new StringBuilder();
275 sb.append("\n");
276
277 String dbInfo = null;
278
279 dbInfo = new ExceptionWrapper(){
280 public String wrappedCall(DatabaseMetaData metaData) throws Exception {
281 return metaData.getDatabaseProductName();
282 }
283 }.executeWrappedCall(metaData);
284 sb.append("\tdatabase product name=").append(dbInfo).append("\n");
285
286 dbInfo = new ExceptionWrapper(){
287 public String wrappedCall(DatabaseMetaData metaData) throws Exception {
288 return metaData.getDatabaseProductVersion();
289 }
290 }.executeWrappedCall(metaData);
291 sb.append("\tdatabase version=").append(dbInfo).append("\n");
292
293 dbInfo = new ExceptionWrapper(){
294 public String wrappedCall(DatabaseMetaData metaData) throws Exception {
295 return String.valueOf(metaData.getDatabaseMajorVersion());
296 }
297 }.executeWrappedCall(metaData);
298 sb.append("\tdatabase major version=").append(dbInfo).append("\n");
299
300 dbInfo = new ExceptionWrapper(){
301 public String wrappedCall(DatabaseMetaData metaData) throws Exception {
302 return String.valueOf(metaData.getDatabaseMinorVersion());
303 }
304 }.executeWrappedCall(metaData);
305 sb.append("\tdatabase minor version=").append(dbInfo).append("\n");
306
307 dbInfo = new ExceptionWrapper(){
308 public String wrappedCall(DatabaseMetaData metaData) throws Exception {
309 return metaData.getDriverName();
310 }
311 }.executeWrappedCall(metaData);
312 sb.append("\tjdbc driver name=").append(dbInfo).append("\n");
313
314 dbInfo = new ExceptionWrapper(){
315 public String wrappedCall(DatabaseMetaData metaData) throws Exception {
316 return metaData.getDriverVersion();
317 }
318 }.executeWrappedCall(metaData);
319 sb.append("\tjdbc driver version=").append(dbInfo).append("\n");
320
321 dbInfo = new ExceptionWrapper(){
322 public String wrappedCall(DatabaseMetaData metaData) throws Exception {
323 return String.valueOf(metaData.getDriverMajorVersion());
324 }
325 }.executeWrappedCall(metaData);
326 sb.append("\tjdbc driver major version=").append(dbInfo).append("\n");
327
328 dbInfo = new ExceptionWrapper(){
329 public String wrappedCall(DatabaseMetaData metaData) throws Exception {
330 return String.valueOf(metaData.getDriverMinorVersion());
331 }
332 }.executeWrappedCall(metaData);
333 sb.append("\tjdbc driver minor version=").append(dbInfo).append("\n");
334
335 return sb.toString();
336 }
337
338
339
340
341
342
343
344 public static void printDatabaseInfo(DatabaseMetaData metaData, PrintStream outputStream) throws SQLException
345 {
346 String dbInfo = getDatabaseInfo(metaData);
347 try {
348 outputStream.println(dbInfo);
349 }
350 finally {
351 outputStream.flush();
352 }
353 }
354
355
356
357
358
359
360
361
362 public static boolean isSybaseDb(DatabaseMetaData metaData) throws SQLException
363 {
364 String dbProductName = metaData.getDatabaseProductName();
365 boolean isSybase = (dbProductName != null && dbProductName.equals(DB_PRODUCT_SYBASE));
366 return isSybase;
367 }
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383 public static final Column createColumn(ResultSet resultSet,
384 IDataTypeFactory dataTypeFactory, boolean datatypeWarning)
385 throws SQLException, DataTypeException
386 {
387 String tableName = resultSet.getString(3);
388 String columnName = resultSet.getString(4);
389 int sqlType = resultSet.getInt(5);
390
391 if(sqlType == java.sql.Types.DISTINCT)
392 {
393 sqlType = resultSet.getInt("SOURCE_DATA_TYPE");
394 }
395
396 String sqlTypeName = resultSet.getString(6);
397
398 int nullable = resultSet.getInt(11);
399 String remarks = resultSet.getString(12);
400 String columnDefaultValue = resultSet.getString(13);
401 String isAutoIncrement = resultSet.getString(23);
402
403 String isGenerated = resultSet.getMetaData().getColumnCount() >= 24 ? resultSet.getString(24) : null;
404
405
406 DataType dataType =
407 dataTypeFactory.createDataType(sqlType, sqlTypeName, tableName, columnName);
408 if (dataType != DataType.UNKNOWN)
409 {
410 Column column = new Column(columnName, dataType,
411 sqlTypeName, Column.nullableValue(nullable), columnDefaultValue, remarks,
412 Column.AutoIncrement.autoIncrementValue(isAutoIncrement),
413 Column.convertMetaDataBoolean(isGenerated));
414 return column;
415 }
416 else
417 {
418 if (datatypeWarning)
419 logger.warn(
420 tableName + "." + columnName +
421 " data type (" + sqlType + ", '" + sqlTypeName +
422 "') not recognized and will be ignored. See FAQ for more information.");
423
424
425 return null;
426 }
427 }
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442 public static boolean matches(ResultSet resultSet,
443 String schema, String table, boolean caseSensitive)
444 throws SQLException
445 {
446 return matches(resultSet, null, schema, table, null, caseSensitive);
447 }
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465 public static boolean matches(ResultSet resultSet,
466 String catalog, String schema,
467 String table, String column, boolean caseSensitive)
468 throws SQLException
469 {
470 String catalogName = resultSet.getString(1);
471 String schemaName = resultSet.getString(2);
472 String tableName = resultSet.getString(3);
473 String columnName = resultSet.getString(4);
474
475
476 if(schema != null && schemaName == null && catalog==null && catalogName != null){
477 logger.debug("Switching catalog/schema because the are mutually null");
478 schemaName = catalogName;
479 catalogName = null;
480 }
481
482 boolean areEqual =
483 areEqualIgnoreNull(catalog, catalogName, caseSensitive) &&
484 areEqualIgnoreNull(schema, schemaName, caseSensitive) &&
485 areEqualIgnoreNull(table, tableName, caseSensitive) &&
486 areEqualIgnoreNull(column, columnName, caseSensitive);
487 return areEqual;
488 }
489
490
491
492
493
494
495
496
497
498
499
500
501 public static final boolean areEqualIgnoreNull(String value1, String value2, boolean caseSensitive)
502 {
503 if(value1==null || value1.equals(""))
504 {
505 return true;
506 }
507 else
508 {
509 if(caseSensitive && value1.equals(value2))
510 {
511 return true;
512 }
513 else if(!caseSensitive && value1.equalsIgnoreCase(value2))
514 {
515 return true;
516 }
517 else
518 {
519 return false;
520 }
521 }
522 }
523
524
525
526
527
528
529
530
531
532
533 public static final String correctCase(final String databaseIdentifier, Connection connection)
534 {
535 logger.trace("correctCase(tableName={}, connection={}) - start", databaseIdentifier, connection);
536
537 try
538 {
539 return correctCase(databaseIdentifier, connection.getMetaData());
540 }
541 catch (SQLException e)
542 {
543 throw new DatabaseUnitRuntimeException("Exception while trying to access database metadata", e);
544 }
545 }
546
547
548
549
550
551
552
553
554
555
556 public static final String correctCase(final String databaseIdentifier, DatabaseMetaData databaseMetaData)
557 {
558 logger.trace("correctCase(tableName={}, databaseMetaData={}) - start", databaseIdentifier, databaseMetaData);
559
560 if (databaseIdentifier == null) {
561 throw new NullPointerException(
562 "The parameter 'databaseIdentifier' must not be null");
563 }
564 if (databaseMetaData == null) {
565 throw new NullPointerException(
566 "The parameter 'databaseMetaData' must not be null");
567 }
568
569 try {
570 String resultTableName = databaseIdentifier;
571 String dbIdentifierQuoteString = databaseMetaData.getIdentifierQuoteString();
572 if(!isEscaped(databaseIdentifier, dbIdentifierQuoteString)){
573 if(databaseMetaData.storesLowerCaseIdentifiers())
574 {
575 resultTableName = databaseIdentifier.toLowerCase(Locale.ENGLISH);
576 }
577 else if(databaseMetaData.storesUpperCaseIdentifiers())
578 {
579 resultTableName = databaseIdentifier.toUpperCase(Locale.ENGLISH);
580 }
581 else
582 {
583 logger.debug("Database does not store upperCase or lowerCase identifiers. " +
584 "Will not correct case of the table names.");
585 }
586 }
587 else
588 {
589 if(logger.isDebugEnabled())
590 logger.debug("The tableName '{}' is escaped. Will not correct case.", databaseIdentifier);
591 }
592 return resultTableName;
593 }
594 catch (SQLException e)
595 {
596 throw new DatabaseUnitRuntimeException("Exception while trying to access database metadata", e);
597 }
598 }
599
600
601
602
603
604
605
606
607
608 public static final void logInfoIfValueChanged(String oldValue, String newValue, String message, Class source)
609 {
610 if(logger.isInfoEnabled())
611 {
612 if(oldValue != null && !oldValue.equals(newValue))
613 logger.debug("{}. {} oldValue={} newValue={}", new Object[] {source, message, oldValue, newValue});
614 }
615 }
616
617
618
619
620
621
622
623
624
625 public static final void logDebugIfValueChanged(String oldValue, String newValue, String message, Class source)
626 {
627 if (logger.isDebugEnabled())
628 {
629 if (oldValue != null && !oldValue.equals(newValue))
630 logger.debug("{}. {} oldValue={} newValue={}", new Object[] {source, message, oldValue, newValue});
631 }
632 }
633
634
635
636
637
638
639
640 private static final boolean isEscaped(String tableName, String dbIdentifierQuoteString)
641 {
642 logger.trace("isEscaped(tableName={}, dbIdentifierQuoteString={}) - start", tableName, dbIdentifierQuoteString);
643
644 if (dbIdentifierQuoteString == null) {
645 throw new NullPointerException(
646 "The parameter 'dbIdentifierQuoteString' must not be null");
647 }
648 boolean isEscaped = tableName!=null && (tableName.startsWith(dbIdentifierQuoteString));
649 if(logger.isDebugEnabled())
650 logger.debug("isEscaped returns '{}' for tableName={} (dbIdentifierQuoteString={})",
651 new Object[]{Boolean.valueOf(isEscaped), tableName, dbIdentifierQuoteString} );
652 return isEscaped;
653 }
654
655
656
657
658
659
660
661
662
663 static abstract class ExceptionWrapper{
664
665 public static final String NOT_AVAILABLE_TEXT = "<not available>";
666
667
668
669
670 public ExceptionWrapper()
671 {
672 }
673
674
675
676
677
678
679 public final String executeWrappedCall(DatabaseMetaData metaData) {
680 try{
681 String result = wrappedCall(metaData);
682 return result;
683 }
684 catch(Exception e){
685 logger.trace("Problem retrieving DB information via DatabaseMetaData", e);
686 return NOT_AVAILABLE_TEXT;
687 }
688 }
689
690
691
692
693
694
695 public abstract String wrappedCall(DatabaseMetaData metaData) throws Exception;
696 }
697
698 }