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 = getSortedUpperTableNames(expectedDataSet);
233         String[] actualNames = getSortedUpperTableNames(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 = actualTable.getRowCount();
368         if (expectedRowsCount != actualRowsCount) {
369             String msg = "row count (table=" + expectedTableName + ")";
370             Error error =
371                     failureHandler.createFailure(msg, String
372                             .valueOf(expectedRowsCount), String
373                             .valueOf(actualRowsCount));
374             logger.error(error.toString());
375             throw error;
376         }
377         // if both tables are empty, it is not necessary to compare columns, as
378         // such
379         // comparison
380         // can fail if column metadata is different (which could occurs when
381         // comparing empty tables)
382         if (expectedRowsCount == 0 && actualRowsCount == 0) {
383             logger.debug("Tables are empty, hence equals.");
384             return;
385         }
386 
387         // Put the columns into the same order
388         Column[] expectedColumns = Columns.getSortedColumns(expectedMetaData);
389         Column[] actualColumns = Columns.getSortedColumns(actualMetaData);
390 
391         // Verify columns
392         Columns.ColumnDiff columnDiff =
393                 Columns.getColumnDiff(expectedMetaData, actualMetaData);
394         if (columnDiff.hasDifference()) {
395             String message = columnDiff.getMessage();
396             Error error =
397                     failureHandler.createFailure(message, Columns
398                             .getColumnNamesAsString(expectedColumns), Columns
399                             .getColumnNamesAsString(actualColumns));
400             logger.error(error.toString());
401             throw error;
402         }
403 
404         // Get the datatypes to be used for comparing the sorted columns
405         ComparisonColumn[] comparisonCols = getComparisonColumns(expectedTableName,
406                 expectedColumns, actualColumns, failureHandler);
407 
408         // Finally compare the data
409         compareData(expectedTable, actualTable, comparisonCols, failureHandler);
410     }
411 
412     /**
413      * @return The default failure handler
414      * @since 2.4
415      */
416     protected FailureHandler getDefaultFailureHandler() 
417     {
418         return getDefaultFailureHandler(null);
419     }
420 
421     /**
422      * @return The default failure handler
423      * @since 2.4
424      */
425     protected FailureHandler getDefaultFailureHandler(Column[] additionalColumnInfo) 
426     {
427         DefaultFailureHandler failureHandler = new DefaultFailureHandler(additionalColumnInfo);
428         if (junitFailureFactory != null) {
429             failureHandler.setFailureFactory(junitFailureFactory);
430         }
431         return failureHandler;
432     }
433 
434     /**
435      * @return the JUnitFailureFactory if JUnit is on the classpath or <code>null</code> if
436      * JUnit is not on the classpath.
437      */
438     private FailureFactory getJUnitFailureFactory() 
439     {
440         try {
441             Class.forName("junit.framework.Assert");
442             // JUnit available
443             return new JUnitFailureFactory();
444         }
445         catch (ClassNotFoundException e) {
446             // JUnit not available on the classpath return null
447             logger.debug("JUnit does not seem to be on the classpath. " + e);
448         }
449         return null;
450     }
451 
452     /**
453      * @param expectedTable
454      *            Table containing all expected results.
455      * @param actualTable
456      *            Table containing all actual results.
457      * @param comparisonCols
458      *            The columns to be compared, also including the correct
459      *            {@link DataType}s for comparison
460      * @param failureHandler
461      *          The failure handler used if the assert fails because of a data
462      *          mismatch. Provides some additional information that may be useful
463      *          to quickly identify the rows for which the mismatch occurred (for
464      *          example by printing an additional primary key column). Must not be
465      *          <code>null</code> at this stage
466      * @throws DataSetException
467      * @since 2.4
468      */
469     protected void compareData(ITable expectedTable, ITable actualTable,
470             ComparisonColumn[] comparisonCols, FailureHandler failureHandler)
471             throws DataSetException
472     {
473         logger.debug("compareData(expectedTable={}, actualTable={}, "
474                 + "comparisonCols={}, failureHandler={}) - start",
475                 new Object[] {expectedTable, actualTable, comparisonCols,
476                         failureHandler});
477 
478         if (expectedTable == null) {
479             throw new NullPointerException(
480                     "The parameter 'expectedTable' must not be null");
481         }
482         if (actualTable == null) {
483             throw new NullPointerException(
484                     "The parameter 'actualTable' must not be null");
485         }
486         if (comparisonCols == null) {
487             throw new NullPointerException(
488                     "The parameter 'comparisonCols' must not be null");
489         }
490         if (failureHandler == null) {
491             throw new NullPointerException(
492                     "The parameter 'failureHandler' must not be null");
493         }
494 
495         // iterate over all rows
496         for (int i = 0; i < expectedTable.getRowCount(); i++) {
497             // iterate over all columns of the current row
498             for (int j = 0; j < comparisonCols.length; j++) {
499                 ComparisonColumn compareColumn = comparisonCols[j];
500 
501                 String columnName = compareColumn.getColumnName();
502                 DataType dataType = compareColumn.getDataType();
503 
504                 Object expectedValue = expectedTable.getValue(i, columnName);
505                 Object actualValue = actualTable.getValue(i, columnName);
506 
507                 // Compare the values
508                 if (skipCompare(columnName, expectedValue, actualValue)) {
509                     if (logger.isTraceEnabled()) {
510                         logger.trace( "ignoring comparison " + expectedValue + "=" +
511                                 actualValue + " on column " + columnName);                        
512                     }
513                     continue;
514                 }
515 
516                 if (dataType.compare(expectedValue, actualValue) != 0) {
517 
518                     Difference diff = new Difference(
519                             expectedTable, actualTable, 
520                             i, columnName, 
521                             expectedValue, actualValue);
522 
523                     // Handle the difference (throw error immediately or something else)
524                     failureHandler.handle(diff);
525                 }
526             }
527         }
528 
529     }
530 
531     /**
532      * Method to last-minute intercept the comparison of a single 
533      * expected and actual value. Designed to be overridden in order
534      * to skip cell comparison by specific cell values.
535      * 
536      * @param columnName The column being compared
537      * @param expectedValue The expected value to be compared
538      * @param actualValue The actual value to be compared
539      * @return <code>false</code> always so that the comparison is never skipped
540      * @since 2.4
541      */
542     protected boolean skipCompare(String columnName, Object expectedValue, Object actualValue) 
543     {
544         return false;
545     }
546 
547     /**
548      * @param expectedTableName
549      * @param expectedColumns
550      * @param actualColumns
551      * @param failureHandler
552      *            The {@link FailureHandler} to be used when no datatype can be
553      *            determined
554      * @return The columns to be used for the assertion, including the correct
555      *         datatype
556      * @since 2.4
557      */
558     protected ComparisonColumn[] getComparisonColumns(String expectedTableName,
559             Column[] expectedColumns, Column[] actualColumns,
560             FailureHandler failureHandler) 
561     {
562         ComparisonColumn[] result = new ComparisonColumn[expectedColumns.length];
563 
564         for (int j = 0; j < expectedColumns.length; j++) {
565             Column expectedColumn = expectedColumns[j];
566             Column actualColumn = actualColumns[j];
567             result[j] = new ComparisonColumn(expectedTableName, expectedColumn,
568                             actualColumn, failureHandler);
569         }
570         return result;
571     }
572 
573     protected String[] getSortedUpperTableNames(IDataSet dataSet)
574     throws DataSetException 
575     {
576         logger.debug("getSortedUpperTableNames(dataSet={}) - start", dataSet);
577 
578         String[] names = dataSet.getTableNames();
579         for (int i = 0; i < names.length; i++) {
580             names[i] = names[i].toUpperCase();
581         }
582         Arrays.sort(names);
583         return names;
584     }
585 
586     /**
587      * Represents a single column to be used for the comparison of table data. It
588      * contains the {@link DataType} to be used for comparing the given column.
589      * This {@link DataType} matches the expected and actual column's datatype.
590      * 
591      * @author gommma (gommma AT users.sourceforge.net)
592      * @author Last changed by: $Author: gommma $
593      * @version $Revision: 864 $ $Date: 2008-11-07 06:27:26 -0800 (Fri, 07 Nov
594      *          2008) $
595      * @since 2.4.0
596      */
597     public static class ComparisonColumn 
598     {
599         /**
600          * Logger for this class
601          */
602         private static final Logger logger = LoggerFactory
603         .getLogger(ComparisonColumn.class);
604 
605         private String columnName;
606         private DataType dataType;
607 
608         /**
609          * @param tableName
610          *            The table name which is only needed for debugging output
611          * @param expectedColumn
612          *          The expected column needed to resolve the {@link DataType} to
613          *          use for the actual comparison
614          * @param actualColumn
615          *          The actual column needed to resolve the {@link DataType} to use
616          *          for the actual comparison
617          * @param failureHandler
618          *          The {@link FailureHandler} to be used when no datatype can be
619          *          determined
620          */
621         public ComparisonColumn(String tableName, Column expectedColumn,
622                 Column actualColumn, FailureHandler failureHandler) {
623             super();
624             this.columnName = expectedColumn.getColumnName();
625             this.dataType = getComparisonDataType(tableName, expectedColumn,
626                             actualColumn, failureHandler);
627         }
628 
629         /**
630          * @return The column actually being compared
631          */
632         public String getColumnName() {
633             return this.columnName;
634         }
635 
636         /**
637          * @return The {@link DataType} to use for the actual comparison
638          */
639         public DataType getDataType() {
640             return this.dataType;
641         }
642 
643         /**
644          * @param tableName
645          *            The table name which is only needed for debugging output
646          * @param expectedColumn
647          * @param actualColumn
648          * @param failureHandler
649          *          The {@link FailureHandler} to be used when no datatype can be
650          *          determined
651          * @return The dbunit {@link DataType} to use for comparing the given
652          *         column.
653          */
654         private DataType getComparisonDataType(String tableName,
655                 Column expectedColumn, Column actualColumn,
656                 FailureHandler failureHandler) {
657             if (logger.isDebugEnabled())
658                 logger.debug(
659                                 "getComparisonDataType(tableName={}, expectedColumn={}, actualColumn={}, failureHandler={}) - start",
660                         new Object[] { tableName, expectedColumn, actualColumn,
661                                 failureHandler });
662 
663             DataType expectedDataType = expectedColumn.getDataType();
664             DataType actualDataType = actualColumn.getDataType();
665 
666             // The two columns have different data type
667             if (!expectedDataType.getClass().isInstance(actualDataType)) {
668                 // Expected column data type is unknown, use actual column data type
669                 if (expectedDataType instanceof UnknownDataType) {
670                     return actualDataType;
671                 }
672 
673                 // Actual column data type is unknown, use expected column data type
674                 if (actualDataType instanceof UnknownDataType) {
675                     return expectedDataType;
676                 }
677 
678                 // Impossible to determine which data type to use
679                 String msg = "Incompatible data types: (table=" + tableName + ", col="
680                 + expectedColumn.getColumnName() + ")";
681                 throw failureHandler.createFailure(msg, String
682                         .valueOf(expectedDataType), String.valueOf(actualDataType));
683             }
684 
685             // Both columns have same data type, return any one of them
686             return expectedDataType;
687         }
688 
689     }
690 
691 }