View Javadoc
1   /*
2    *
3    * The DbUnit Database Testing Framework
4    * Copyright (C)2002-2008, DbUnit.org
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2.1 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General Public License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   *
20   */
21  package org.dbunit.assertion;
22  
23  import java.sql.SQLException;
24  import java.util.Arrays;
25  
26  import org.dbunit.Assertion;
27  import org.dbunit.DatabaseUnitException;
28  import org.dbunit.database.IDatabaseConnection;
29  import org.dbunit.dataset.Column;
30  import org.dbunit.dataset.Columns;
31  import org.dbunit.dataset.DataSetException;
32  import org.dbunit.dataset.IDataSet;
33  import org.dbunit.dataset.ITable;
34  import org.dbunit.dataset.ITableMetaData;
35  import org.dbunit.dataset.datatype.DataType;
36  import org.dbunit.dataset.datatype.UnknownDataType;
37  import org.dbunit.dataset.filter.DefaultColumnFilter;
38  import org.slf4j.Logger;
39  import org.slf4j.LoggerFactory;
40  
41  /**
42   * Default implementation of DbUnit assertions, based on the original methods present
43   * at {@link Assertion}
44   * 
45   * @author Felipe Leme (dbunit@felipeal.net)
46   * @author gommma (gommma AT users.sourceforge.net)
47   * @version $Revision$ $Date$
48   * @since 2.4.0
49   */
50  public class DbUnitAssert
51  {
52  
53      /**
54       * Logger for this class
55       */
56      private static final Logger logger = LoggerFactory.getLogger(DbUnitAssert.class);
57  
58      private FailureFactory junitFailureFactory = getJUnitFailureFactory();
59  
60      /**
61       * Default constructor
62       */
63      public DbUnitAssert()
64      {
65      }
66  
67      /**
68       * Compare one table present in two datasets ignoring specified columns.
69       * 
70       * @param expectedDataset
71       *            First dataset.
72       * @param actualDataset
73       *            Second dataset.
74       * @param tableName
75       *            Table name of the table to be compared.
76       * @param ignoreCols
77       *            Columns to be ignored in comparison.
78       * @throws org.dbunit.DatabaseUnitException
79       *             If an error occurs.
80       */
81      public void assertEqualsIgnoreCols(final IDataSet expectedDataset,
82              final IDataSet actualDataset, final String tableName,
83              final String[] ignoreCols) throws DatabaseUnitException 
84      {
85          if (logger.isDebugEnabled())
86              logger.debug(
87                              "assertEqualsIgnoreCols(expectedDataset={}, actualDataset={}, tableName={}, ignoreCols={}) - start",
88                      new Object[] { expectedDataset, actualDataset, tableName,
89                              Arrays.asList(ignoreCols) });
90  
91          assertEqualsIgnoreCols(expectedDataset.getTable(tableName), actualDataset
92                  .getTable(tableName), ignoreCols);
93      }
94  
95      /**
96       * Compare the given tables ignoring specified columns.
97       * 
98       * @param expectedTable
99       *            First table.
100      * @param actualTable
101      *            Second table.
102      * @param ignoreCols
103      *            Columns to be ignored in comparison.
104      * @throws org.dbunit.DatabaseUnitException
105      *             If an error occurs.
106      */
107     public void assertEqualsIgnoreCols(final ITable expectedTable,
108             final ITable actualTable, final String[] ignoreCols)
109     throws DatabaseUnitException 
110     {
111         if (logger.isDebugEnabled())
112             logger
113                     .debug(
114                             "assertEqualsIgnoreCols(expectedTable={}, actualTable={}, ignoreCols={}) - start",
115                             new Object[] {expectedTable, actualTable,
116                                     Arrays.asList(ignoreCols)});
117 
118         final ITable expectedTableFiltered = DefaultColumnFilter
119                 .excludedColumnsTable(expectedTable, ignoreCols);
120         final ITable actualTableFiltered = DefaultColumnFilter
121                 .excludedColumnsTable(actualTable, ignoreCols);
122         assertEquals(expectedTableFiltered, actualTableFiltered);
123     }
124 
125     /**
126      * Compare a table from a dataset with a table generated from an sql query.
127      * 
128      * @param expectedDataset
129      *            Dataset to retrieve the first table from.
130      * @param connection
131      *            Connection to use for the SQL statement.
132      * @param sqlQuery
133      *          SQL query that will build the data in returned second table rows.
134      * @param tableName
135      *            Table name of the table to compare
136      * @param ignoreCols
137      *            Columns to be ignored in comparison.
138      * @throws DatabaseUnitException
139      *             If an error occurs while performing the comparison.
140      * @throws java.sql.SQLException
141      *             If an SQL error occurs.
142      */
143     public void assertEqualsByQuery(final IDataSet expectedDataset,
144             final IDatabaseConnection connection, final String sqlQuery,
145             final String tableName, final String[] ignoreCols)
146     throws DatabaseUnitException, SQLException 
147     {
148         if (logger.isDebugEnabled())
149             logger.debug(
150                     "assertEqualsByQuery(expectedDataset={}, connection={}, tableName={}, sqlQuery={}, ignoreCols={}) - start",
151                     new Object[] { expectedDataset, connection, tableName, sqlQuery,
152                             ignoreCols });
153 
154         ITable expectedTable = expectedDataset.getTable(tableName);
155         assertEqualsByQuery(expectedTable, connection, tableName, sqlQuery,
156                 ignoreCols);
157     }
158 
159     /**
160      * Compare a table with a table generated from an sql query.
161      * 
162      * @param expectedTable
163      *            Table containing all expected results.
164      * @param connection
165      *            Connection to use for the SQL statement.
166      * @param tableName
167      *            The name of the table to query from the database
168      * @param sqlQuery
169      *          SQL query that will build the data in returned second table rows.
170      * @param ignoreCols
171      *            Columns to be ignored in comparison.
172      * @throws DatabaseUnitException
173      *             If an error occurs while performing the comparison.
174      * @throws java.sql.SQLException
175      *             If an SQL error occurs.
176      */
177     public void assertEqualsByQuery(final ITable expectedTable,
178             final IDatabaseConnection connection, final String tableName,
179             final String sqlQuery, final String[] ignoreCols)
180     throws DatabaseUnitException, SQLException 
181     {
182         if (logger.isDebugEnabled())
183             logger.debug(
184                     "assertEqualsByQuery(expectedTable={}, connection={}, tableName={}, sqlQuery={}, ignoreCols={}) - start",
185                     new Object[] { expectedTable, connection, tableName, sqlQuery,
186                             ignoreCols });
187 
188         ITable expected = DefaultColumnFilter.excludedColumnsTable(expectedTable,
189                         ignoreCols);
190         ITable queriedTable = connection.createQueryTable(tableName, sqlQuery);
191         ITable actual = DefaultColumnFilter.excludedColumnsTable(queriedTable,
192                         ignoreCols);
193         assertEquals(expected, actual);
194     }
195 
196     /**
197      * Asserts that the two specified dataset are equals. This method ignore the
198      * tables order.
199      */
200     public void assertEquals(IDataSet expectedDataSet, IDataSet actualDataSet)
201     throws DatabaseUnitException 
202     {
203         logger.debug("assertEquals(expectedDataSet={}, actualDataSet={}) - start",
204                 expectedDataSet, actualDataSet);
205         assertEquals(expectedDataSet, actualDataSet, null);
206     }
207 
208     /**
209      * Asserts that the two specified dataset are equals. This method ignore the
210      * tables order.
211      * 
212      * @since 2.4
213      */
214     public void assertEquals(IDataSet expectedDataSet, IDataSet actualDataSet,
215             FailureHandler failureHandler) throws DatabaseUnitException 
216     {
217         if (logger.isDebugEnabled())
218             logger.debug(
219                     "assertEquals(expectedDataSet={}, actualDataSet={}, failureHandler={}) - start",
220                     new Object[] { expectedDataSet, actualDataSet, failureHandler });
221 
222         // do not continue if same instance
223         if (expectedDataSet == actualDataSet) {
224             return;
225         }
226 
227         if (failureHandler == null) {
228             logger.debug("FailureHandler is null. Using default implementation");
229             failureHandler = getDefaultFailureHandler();
230         }
231 
232         String[] expectedNames = getSortedTableNames(expectedDataSet);
233         String[] actualNames = getSortedTableNames(actualDataSet);
234 
235         // tables count
236         if (expectedNames.length != actualNames.length) {
237             throw failureHandler.createFailure("table count", String
238                     .valueOf(expectedNames.length), String.valueOf(actualNames.length));
239         }
240 
241         // table names in no specific order
242         for (int i = 0; i < expectedNames.length; i++) {
243             if (!actualNames[i].equals(expectedNames[i])) {
244                 throw failureHandler.createFailure("tables", Arrays.asList(
245                         expectedNames).toString(), Arrays.asList(actualNames).toString());
246             }
247 
248         }
249 
250         // tables
251         for (int i = 0; i < expectedNames.length; i++) {
252             String name = expectedNames[i];
253             assertEquals(expectedDataSet.getTable(name), actualDataSet.getTable(name), failureHandler);
254         }
255 
256     }
257 
258     /**
259      * Asserts that the two specified tables are equals. This method ignores the
260      * table names, the columns order, the columns data type and which columns are
261      * composing the primary keys.
262      * 
263      * @param expectedTable
264      *            Table containing all expected results.
265      * @param actualTable
266      *            Table containing all actual results.
267      * @throws DatabaseUnitException
268      */
269     public void assertEquals(ITable expectedTable, ITable actualTable)
270     throws DatabaseUnitException 
271     {
272         logger.debug("assertEquals(expectedTable={}, actualTable={}) - start",
273                 expectedTable, actualTable);
274         assertEquals(expectedTable, actualTable, (Column[]) null);
275     }
276 
277     /**
278      * Asserts that the two specified tables are equals. This method ignores the
279      * table names, the columns order, the columns data type and which columns are
280      * composing the primary keys. <br />
281      * Example: <code><pre>
282      * ITable actualTable = ...;
283      * ITable expectedTable = ...;
284      * ITableMetaData metaData = actualTable.getTableMetaData();
285      * Column[] additionalInfoCols = Columns.getColumns(new String[] {"MY_PK_COLUMN"}, metaData.getColumns());
286      * assertEquals(expectedTable, actualTable, additionalInfoCols);
287      * </pre></code>
288      * 
289      * @param expectedTable
290      *            Table containing all expected results.
291      * @param actualTable
292      *            Table containing all actual results.
293      * @param additionalColumnInfo
294      *          The columns to be printed out if the assert fails because of a
295      *          data mismatch. Provides some additional column values that may be
296      *          useful to quickly identify the columns for which the mismatch
297      *          occurred (for example a primary key column). Can be
298      *            <code>null</code>
299      * @throws DatabaseUnitException
300      */
301     public void assertEquals(ITable expectedTable, ITable actualTable,
302             Column[] additionalColumnInfo) throws DatabaseUnitException 
303     {
304         logger.debug(
305                 "assertEquals(expectedTable={}, actualTable={}, additionalColumnInfo={}) - start",
306                 new Object[] { expectedTable, actualTable, additionalColumnInfo });
307 
308         FailureHandler failureHandler = null;
309         if (additionalColumnInfo != null)
310             failureHandler = getDefaultFailureHandler(additionalColumnInfo);
311 
312         assertEquals(expectedTable, actualTable, failureHandler);
313     }
314 
315     /**
316      * Asserts that the two specified tables are equals. This method ignores the
317      * table names, the columns order, the columns data type and which columns are
318      * composing the primary keys. <br />
319      * Example: <code><pre>
320      * ITable actualTable = ...;
321      * ITable expectedTable = ...;
322      * ITableMetaData metaData = actualTable.getTableMetaData();
323      * FailureHandler failureHandler = new DefaultFailureHandler();
324      * assertEquals(expectedTable, actualTable, failureHandler);
325      * </pre></code>
326      * 
327      * @param expectedTable
328      *            Table containing all expected results.
329      * @param actualTable
330      *            Table containing all actual results.
331      * @param failureHandler
332      *          The failure handler used if the assert fails because of a data
333      *          mismatch. Provides some additional information that may be useful
334      *          to quickly identify the rows for which the mismatch occurred (for
335      *          example by printing an additional primary key column). Can be
336      *          <code>null</code>
337      * @throws DatabaseUnitException
338      * @since 2.4
339      */
340     public void assertEquals(ITable expectedTable, ITable actualTable,
341             FailureHandler failureHandler) throws DatabaseUnitException
342     {
343         logger.trace("assertEquals(expectedTable, actualTable, failureHandler) - start");
344         logger.debug("assertEquals: expectedTable={}", expectedTable);
345         logger.debug("assertEquals: actualTable={}", actualTable);
346         logger.debug("assertEquals: failureHandler={}", failureHandler);
347 
348         // Do not continue if same instance
349         if (expectedTable == actualTable) {
350             logger.debug(
351                     "The given tables reference the same object. Will return immediately. (Table={})",
352                     expectedTable);
353             return;
354         }
355 
356         if (failureHandler == null) {
357             logger.debug("FailureHandler is null. Using default implementation");
358             failureHandler = getDefaultFailureHandler();
359         }
360 
361         ITableMetaData expectedMetaData = expectedTable.getTableMetaData();
362         ITableMetaData actualMetaData = actualTable.getTableMetaData();
363         String expectedTableName = expectedMetaData.getTableName();
364 
365         // Verify row count
366         int expectedRowsCount = expectedTable.getRowCount();
367         int actualRowsCount = 0;
368         boolean skipRowComparison = false;
369         try {
370             actualRowsCount = actualTable.getRowCount();
371         } catch(UnsupportedOperationException exception) {
372             skipRowComparison = true;
373         }
374         if(!skipRowComparison) {
375             if (expectedRowsCount != actualRowsCount) {
376                 String msg = "row count (table=" + expectedTableName + ")";
377                 Error error =
378                         failureHandler.createFailure(msg, String
379                                 .valueOf(expectedRowsCount), String
380                                 .valueOf(actualRowsCount));
381                 logger.error(error.toString());
382                 throw error;
383             }
384             // if both tables are empty, it is not necessary to compare columns, as
385             // such comparison can fail if column metadata is different (which
386             // could occurs when comparing empty tables)
387             if (expectedRowsCount == 0 && actualRowsCount == 0) {
388                 logger.debug("Tables are empty, hence equals.");
389                 return;
390             }
391         }
392 
393         // Put the columns into the same order
394         Column[] expectedColumns = Columns.getSortedColumns(expectedMetaData);
395         Column[] actualColumns = Columns.getSortedColumns(actualMetaData);
396 
397         // Verify columns
398         Columns.ColumnDiff columnDiff =
399                 Columns.getColumnDiff(expectedMetaData, actualMetaData);
400         if (columnDiff.hasDifference()) {
401             String message = columnDiff.getMessage();
402             Error error =
403                     failureHandler.createFailure(message, Columns
404                             .getColumnNamesAsString(expectedColumns), Columns
405                             .getColumnNamesAsString(actualColumns));
406             logger.error(error.toString());
407             throw error;
408         }
409 
410         // Get the datatypes to be used for comparing the sorted columns
411         ComparisonColumn[] comparisonCols = getComparisonColumns(expectedTableName,
412                 expectedColumns, actualColumns, failureHandler);
413 
414         // Finally compare the data
415         compareData(expectedTable, actualTable, comparisonCols, failureHandler);
416     }
417 
418     /**
419      * @return The default failure handler
420      * @since 2.4
421      */
422     protected FailureHandler getDefaultFailureHandler() 
423     {
424         return getDefaultFailureHandler(null);
425     }
426 
427     /**
428      * @return The default failure handler
429      * @since 2.4
430      */
431     protected FailureHandler getDefaultFailureHandler(Column[] additionalColumnInfo) 
432     {
433         DefaultFailureHandler failureHandler = new DefaultFailureHandler(additionalColumnInfo);
434         if (junitFailureFactory != null) {
435             failureHandler.setFailureFactory(junitFailureFactory);
436         }
437         return failureHandler;
438     }
439 
440     /**
441      * @return the JUnitFailureFactory if JUnit is on the classpath or <code>null</code> if
442      * JUnit is not on the classpath.
443      */
444     private FailureFactory getJUnitFailureFactory() 
445     {
446         try {
447             Class.forName("junit.framework.Assert");
448             // JUnit available
449             return new JUnitFailureFactory();
450         }
451         catch (ClassNotFoundException e) {
452             // JUnit not available on the classpath return null
453             logger.debug("JUnit does not seem to be on the classpath. " + e);
454         }
455         return null;
456     }
457 
458     /**
459      * @param expectedTable
460      *            Table containing all expected results.
461      * @param actualTable
462      *            Table containing all actual results.
463      * @param comparisonCols
464      *            The columns to be compared, also including the correct
465      *            {@link DataType}s for comparison
466      * @param failureHandler
467      *          The failure handler used if the assert fails because of a data
468      *          mismatch. Provides some additional information that may be useful
469      *          to quickly identify the rows for which the mismatch occurred (for
470      *          example by printing an additional primary key column). Must not be
471      *          <code>null</code> at this stage
472      * @throws DataSetException
473      * @since 2.4
474      */
475     protected void compareData(ITable expectedTable, ITable actualTable,
476             ComparisonColumn[] comparisonCols, FailureHandler failureHandler)
477             throws DataSetException
478     {
479         logger.debug("compareData(expectedTable={}, actualTable={}, "
480                 + "comparisonCols={}, failureHandler={}) - start",
481                 new Object[] {expectedTable, actualTable, comparisonCols,
482                         failureHandler});
483 
484         if (expectedTable == null) {
485             throw new NullPointerException(
486                     "The parameter 'expectedTable' must not be null");
487         }
488         if (actualTable == null) {
489             throw new NullPointerException(
490                     "The parameter 'actualTable' must not be null");
491         }
492         if (comparisonCols == null) {
493             throw new NullPointerException(
494                     "The parameter 'comparisonCols' must not be null");
495         }
496         if (failureHandler == null) {
497             throw new NullPointerException(
498                     "The parameter 'failureHandler' must not be null");
499         }
500 
501         // iterate over all rows
502         for (int i = 0; i < expectedTable.getRowCount(); i++) {
503             // iterate over all columns of the current row
504             for (int j = 0; j < comparisonCols.length; j++) {
505                 ComparisonColumn compareColumn = comparisonCols[j];
506 
507                 String columnName = compareColumn.getColumnName();
508                 DataType dataType = compareColumn.getDataType();
509 
510                 Object expectedValue = expectedTable.getValue(i, columnName);
511                 Object actualValue = actualTable.getValue(i, columnName);
512 
513                 // Compare the values
514                 if (skipCompare(columnName, expectedValue, actualValue)) {
515                     if (logger.isTraceEnabled()) {
516                         logger.trace( "ignoring comparison " + expectedValue + "=" +
517                                 actualValue + " on column " + columnName);
518                     }
519                     continue;
520                 }
521 
522                 if (dataType.compare(expectedValue, actualValue) != 0) {
523 
524                     Difference diff = new Difference(
525                             expectedTable, actualTable, 
526                             i, columnName, 
527                             expectedValue, actualValue);
528 
529                     // Handle the difference (throw error immediately or something else)
530                     failureHandler.handle(diff);
531                 }
532             }
533         }
534 
535     }
536 
537     /**
538      * Method to last-minute intercept the comparison of a single 
539      * expected and actual value. Designed to be overridden in order
540      * to skip cell comparison by specific cell values.
541      * 
542      * @param columnName The column being compared
543      * @param expectedValue The expected value to be compared
544      * @param actualValue The actual value to be compared
545      * @return <code>false</code> always so that the comparison is never skipped
546      * @since 2.4
547      */
548     protected boolean skipCompare(String columnName, Object expectedValue, Object actualValue) 
549     {
550         return false;
551     }
552 
553     /**
554      * @param expectedTableName
555      * @param expectedColumns
556      * @param actualColumns
557      * @param failureHandler
558      *            The {@link FailureHandler} to be used when no datatype can be
559      *            determined
560      * @return The columns to be used for the assertion, including the correct
561      *         datatype
562      * @since 2.4
563      */
564     protected ComparisonColumn[] getComparisonColumns(String expectedTableName,
565             Column[] expectedColumns, Column[] actualColumns,
566             FailureHandler failureHandler) 
567     {
568         ComparisonColumn[] result = new ComparisonColumn[expectedColumns.length];
569 
570         for (int j = 0; j < expectedColumns.length; j++) {
571             Column expectedColumn = expectedColumns[j];
572             Column actualColumn = actualColumns[j];
573             result[j] = new ComparisonColumn(expectedTableName, expectedColumn,
574                             actualColumn, failureHandler);
575         }
576         return result;
577     }
578 
579     protected String[] getSortedTableNames(IDataSet dataSet)
580     throws DataSetException 
581     {
582         logger.debug("getSortedTableNames(dataSet={}) - start", dataSet);
583 
584         String[] names = dataSet.getTableNames();
585         if( !dataSet.isCaseSensitiveTableNames() ) {
586 	        for (int i = 0; i < names.length; i++) {
587 	            names[i] = names[i].toUpperCase();
588 	        }
589         }
590         Arrays.sort(names);
591         return names;
592     }
593 
594     /**
595      * Represents a single column to be used for the comparison of table data. It
596      * contains the {@link DataType} to be used for comparing the given column.
597      * This {@link DataType} matches the expected and actual column's datatype.
598      * 
599      * @author gommma (gommma AT users.sourceforge.net)
600      * @author Last changed by: $Author: gommma $
601      * @version $Revision: 864 $ $Date: 2008-11-07 06:27:26 -0800 (Fri, 07 Nov
602      *          2008) $
603      * @since 2.4.0
604      */
605     public static class ComparisonColumn 
606     {
607         /**
608          * Logger for this class
609          */
610         private static final Logger logger = LoggerFactory
611         .getLogger(ComparisonColumn.class);
612 
613         private String columnName;
614         private DataType dataType;
615 
616         /**
617          * @param tableName
618          *            The table name which is only needed for debugging output
619          * @param expectedColumn
620          *          The expected column needed to resolve the {@link DataType} to
621          *          use for the actual comparison
622          * @param actualColumn
623          *          The actual column needed to resolve the {@link DataType} to use
624          *          for the actual comparison
625          * @param failureHandler
626          *          The {@link FailureHandler} to be used when no datatype can be
627          *          determined
628          */
629         public ComparisonColumn(String tableName, Column expectedColumn,
630                 Column actualColumn, FailureHandler failureHandler) {
631             super();
632             this.columnName = expectedColumn.getColumnName();
633             this.dataType = getComparisonDataType(tableName, expectedColumn,
634                             actualColumn, failureHandler);
635         }
636 
637         /**
638          * @return The column actually being compared
639          */
640         public String getColumnName() {
641             return this.columnName;
642         }
643 
644         /**
645          * @return The {@link DataType} to use for the actual comparison
646          */
647         public DataType getDataType() {
648             return this.dataType;
649         }
650 
651         /**
652          * @param tableName
653          *            The table name which is only needed for debugging output
654          * @param expectedColumn
655          * @param actualColumn
656          * @param failureHandler
657          *          The {@link FailureHandler} to be used when no datatype can be
658          *          determined
659          * @return The dbunit {@link DataType} to use for comparing the given
660          *         column.
661          */
662         private DataType getComparisonDataType(String tableName,
663                 Column expectedColumn, Column actualColumn,
664                 FailureHandler failureHandler) {
665             if (logger.isDebugEnabled())
666                 logger.debug(
667                         "getComparisonDataType(tableName={}, expectedColumn={}, actualColumn={}, failureHandler={}) - start",
668                         new Object[] { tableName, expectedColumn, actualColumn,
669                                 failureHandler });
670 
671             DataType expectedDataType = expectedColumn.getDataType();
672             DataType actualDataType = actualColumn.getDataType();
673 
674             // The two columns have different data type
675             if (!expectedDataType.getClass().isInstance(actualDataType)) {
676                 // Expected column data type is unknown, use actual column data type
677                 if (expectedDataType instanceof UnknownDataType) {
678                     return actualDataType;
679                 }
680 
681                 // Actual column data type is unknown, use expected column data type
682                 if (actualDataType instanceof UnknownDataType) {
683                     return expectedDataType;
684                 }
685 
686                 // Impossible to determine which data type to use
687                 String msg = "Incompatible data types: (table=" + tableName + ", col="
688                         + expectedColumn.getColumnName() + ")";
689                 throw failureHandler.createFailure(msg, String
690                         .valueOf(expectedDataType), String.valueOf(actualDataType));
691             }
692 
693             // Both columns have same data type, return any one of them
694             return expectedDataType;
695         }
696 
697     }
698 
699 }