View Javadoc
1   /*
2    *
3    * The DbUnit Database Testing Framework
4    * Copyright (C)2002-2004, 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.database;
22  
23  import static org.assertj.core.api.Assertions.assertThat;
24  import static org.junit.jupiter.api.Assertions.assertThrows;
25  import static org.junit.jupiter.api.Assertions.fail;
26  
27  import java.util.Arrays;
28  
29  import org.dbunit.AbstractDatabaseIT;
30  import org.junit.jupiter.api.Assumptions;
31  import org.dbunit.DdlExecutor;
32  import org.dbunit.H2Environment;
33  import org.dbunit.dataset.FilteredDataSet;
34  import org.dbunit.dataset.IDataSet;
35  import org.dbunit.dataset.filter.ITableFilter;
36  import org.dbunit.dataset.filter.IncludeTableFilter;
37  import org.dbunit.ext.h2.H2Connection;
38  import org.dbunit.testutil.TestUtils;
39  import org.junit.jupiter.api.Test;
40  
41  import java.sql.Connection;
42  
43  /**
44   * @author Manuel Laflamme
45   * @since May 8, 2004
46   * @version $Revision$
47   */
48  class DatabaseSequenceFilterIT extends AbstractDatabaseIT
49  {
50  
51      /**
52       * Replaces {@code _connection} with a fresh connection after test tables
53       * have been dropped, so that {@code AbstractDatabaseIT.tearDown()} uses an
54       * up-to-date dataset that does not include the dropped tables.
55       *
56       * @throws Exception
57       */
58      private void refreshConnection() throws Exception
59      {
60          _connection.close();
61          _connection = getDatabaseTester().getConnection();
62          setUpDatabaseConfig(_connection.getConfig());
63      }
64  
65      @Test
66      void testGetTableNames_withFkConstrainedTables_returnsDependencyOrderedNames() throws Exception
67      {
68          final String[] testTableNames =
69                  {"A", "B", "C", "D", "E", "F", "G", "H"};
70          final String[] expectedNoFilter =
71                  {"A", "B", "C", "D", "E", "F", "G", "H",};
72          final String[] expectedFiltered =
73                  {"D", "A", "F", "C", "G", "E", "H", "B",};
74  
75          DdlExecutor.dropTables(_connection.getConnection(), "B", "C", "E",
76                  "H", "F", "G", "A", "D");
77          DdlExecutor.executeDdlFile(TestUtils.getFile("sql/hypersonic_fk.sql"),
78                  _connection.getConnection(), false);
79          try
80          {
81              final IDataSet allTables = _connection.createDataSet();
82              final IDataSet databaseDataset =
83                      new FilteredDataSet(new IncludeTableFilter(testTableNames),
84                              allTables);
85              final String[] actualNoFilter = databaseDataset.getTableNames();
86              assertThat(actualNoFilter).as("no filter")
87                      .usingElementComparator(String.CASE_INSENSITIVE_ORDER)
88                      .containsExactly(expectedNoFilter);
89  
90              final ITableFilter filter =
91                      new DatabaseSequenceFilter(_connection);
92              final IDataSet filteredDataSet =
93                      new FilteredDataSet(filter, databaseDataset);
94              final String[] actualFiltered = filteredDataSet.getTableNames();
95              assertThat(actualFiltered).as("filtered")
96                      .usingElementComparator(String.CASE_INSENSITIVE_ORDER)
97                      .containsExactly(expectedFiltered);
98          }
99          finally
100         {
101             DdlExecutor.dropTables(_connection.getConnection(), "B", "C", "E",
102                     "H", "F", "G", "A", "D");
103             refreshConnection();
104         }
105     }
106 
107     @Test
108     void testGetTableNames_withCyclicFkConstraints_throwsCyclicTablesDependencyException() throws Exception
109     {
110         final String[] testTableNames = {"A", "B", "C", "D", "E"};
111         final String[] expectedNoFilter = {"A", "B", "C", "D", "E",};
112 
113         DdlExecutor.dropTables(_connection.getConnection(), "A", "B", "C",
114                 "D", "E");
115         DdlExecutor.executeDdlFile(
116                 TestUtils.getFile("sql/hypersonic_cyclic.sql"),
117                 _connection.getConnection(), false);
118         try
119         {
120             final IDataSet allTables = _connection.createDataSet();
121             final IDataSet databaseDataset =
122                     new FilteredDataSet(new IncludeTableFilter(testTableNames),
123                             allTables);
124             final String[] actualNoFilter = databaseDataset.getTableNames();
125             assertThat(actualNoFilter).as("no filter")
126                     .usingElementComparator(String.CASE_INSENSITIVE_ORDER)
127                     .containsExactly(expectedNoFilter);
128 
129             assertThrows(CyclicTablesDependencyException.class, () -> {
130                 final ITableFilter filter =
131                         new DatabaseSequenceFilter(_connection);
132                 final IDataSet filteredDataSet =
133                         new FilteredDataSet(filter, databaseDataset);
134                 filteredDataSet.getTableNames();
135                 fail("Should not be here!");
136             }, "Expected CyclicTablesDependencyException was not raised");
137         }
138         finally
139         {
140             DdlExecutor.dropTables(_connection.getConnection(), "A", "B", "C",
141                     "D", "E");
142             refreshConnection();
143         }
144     }
145 
146     @Test
147     void testGetTableNames_withCyclicFkConstraintsAndSkipCycleCheckFeatureEnabled_doesNotThrow()
148             throws Exception
149     {
150         final String[] testTableNames = {"A", "B", "C", "D", "E"};
151 
152         DdlExecutor.dropTables(_connection.getConnection(), "A", "B", "C",
153                 "D", "E");
154         DdlExecutor.executeDdlFile(
155                 TestUtils.getFile("sql/hypersonic_cyclic.sql"),
156                 _connection.getConnection(), false);
157         try
158         {
159             _connection.getConfig().setFeature(DatabaseConfig.FEATURE_SKIP_CYCLE_CHECK, true);
160             final IDataSet allTables = _connection.createDataSet();
161             final IDataSet databaseDataset =
162                     new FilteredDataSet(new IncludeTableFilter(testTableNames),
163                             allTables);
164 
165             final ITableFilter filter = new DatabaseSequenceFilter(_connection);
166             final IDataSet filteredDataSet =
167                     new FilteredDataSet(filter, databaseDataset);
168             final String[] actualFiltered = filteredDataSet.getTableNames();
169             final String[] expectedFiltered = {"B", "A", "C", "D", "E"};
170 
171             assertThat(actualFiltered)
172                     .as("FEATURE_SKIP_CYCLE_CHECK must let the A/C/D/E cycle through without "
173                             + "throwing, sorting independent table B before the cycle it is "
174                             + "really depended on by (via C's FK), and falling back to input "
175                             + "order (A, C, D, E) for the cycle's own members.")
176                     .usingElementComparator(String.CASE_INSENSITIVE_ORDER)
177                     .containsExactly(expectedFiltered);
178         }
179         finally
180         {
181             DdlExecutor.dropTables(_connection.getConnection(), "A", "B", "C",
182                     "D", "E");
183             refreshConnection();
184         }
185     }
186 
187     @Test
188     void testGetTableNames_withCaseSensitiveFeatureEnabled_returnsMixedCaseTableNames() throws Exception
189     {
190         final java.sql.DatabaseMetaData dbMeta =
191                 _connection.getConnection().getMetaData();
192         Assumptions.assumeTrue(
193                 dbMeta.supportsMixedCaseQuotedIdentifiers(),
194                 "Skip: database does not treat quoted identifiers as case-sensitive.");
195         Assumptions.assumeTrue(
196                 "\"".equals(dbMeta.getIdentifierQuoteString()),
197                 "Skip: database does not use ANSI double-quote identifier syntax.");
198         Assumptions.assumeFalse(
199                 dbMeta.storesLowerCaseIdentifiers(),
200                 "Skip: database stores unquoted identifiers in lowercase.");
201         final String[] testTableNames = {"MixedCaseTable", "UPPER_CASE_TABLE"};
202         final String[] expectedNoFilter =
203                 {"MixedCaseTable", "UPPER_CASE_TABLE"};
204         final String[] expectedFiltered =
205                 {"MixedCaseTable", "UPPER_CASE_TABLE"};
206 
207         DdlExecutor.dropTables(_connection.getConnection(), "UPPER_CASE_TABLE");
208         try
209         {
210             DdlExecutor.executeSql(_connection.getConnection(),
211                     "DROP TABLE \"MixedCaseTable\"");
212         }
213         catch (final Exception ignored)
214         {
215             logger.debug("Pre-drop of MixedCaseTable ignored: {}",
216                     ignored.getMessage());
217         }
218         DdlExecutor.executeDdlFile(
219                 TestUtils.getFile("sql/hypersonic_case_sensitive_test.sql"),
220                 _connection.getConnection(), false);
221         try
222         {
223             _connection.getConfig().setProperty(
224                     DatabaseConfig.FEATURE_CASE_SENSITIVE_TABLE_NAMES,
225                     Boolean.TRUE);
226 
227             final IDataSet allTables = _connection.createDataSet();
228             final IDataSet databaseDataset =
229                     new FilteredDataSet(new IncludeTableFilter(testTableNames),
230                             allTables);
231             final String[] actualNoFilter = databaseDataset.getTableNames();
232             assertThat(Arrays.asList(actualNoFilter)).as("no filter")
233                     .isEqualTo(Arrays.asList(expectedNoFilter));
234 
235             final ITableFilter filter =
236                     new DatabaseSequenceFilter(_connection);
237             final IDataSet filteredDataSet =
238                     new FilteredDataSet(filter, databaseDataset);
239             final String[] actualFiltered = filteredDataSet.getTableNames();
240             assertThat(Arrays.asList(actualFiltered)).as("filtered")
241                     .isEqualTo(Arrays.asList(expectedFiltered));
242         }
243         finally
244         {
245             _connection.getConfig().setProperty(
246                     DatabaseConfig.FEATURE_CASE_SENSITIVE_TABLE_NAMES,
247                     Boolean.FALSE);
248             DdlExecutor.dropTables(_connection.getConnection(),
249                     "UPPER_CASE_TABLE");
250             try
251             {
252                 DdlExecutor.executeSql(_connection.getConnection(),
253                         "DROP TABLE \"MixedCaseTable\"");
254             }
255             catch (final Exception ignored)
256             {
257                 logger.debug("Post-drop of MixedCaseTable ignored: {}",
258                         ignored.getMessage());
259             }
260             refreshConnection();
261         }
262     }
263 
264     /**
265      * Note that this test uses the H2 database because we could not find out
266      * how to create 2 separate schemas in the hsqldb in memory DB.
267      *
268      * @throws Exception
269      */
270     @Test
271     void testGetTableNames_withMultiSchemaTables_respectsForeignKeyOrder() throws Exception
272     {
273         final Connection jdbcConnection =
274                 H2Environment.createJdbcConnection("test");
275         DdlExecutor.executeDdlFile(
276                 TestUtils.getFile("sql/h2_multischema_fk_test.sql"),
277                 jdbcConnection);
278         final IDatabaseConnection connection = new H2Connection(jdbcConnection, null);
279         connection.getConfig().setProperty(
280                 DatabaseConfig.FEATURE_QUALIFIED_TABLE_NAMES, Boolean.TRUE);
281 
282         final IDataSet databaseDataset = connection.createDataSet();
283         final ITableFilter filter = new DatabaseSequenceFilter(connection);
284         final IDataSet filteredDataSet =
285                 new FilteredDataSet(filter, databaseDataset);
286 
287         final String[] actualNoFilter = databaseDataset.getTableNames();
288         assertThat(actualNoFilter).hasSize(2);
289         assertThat(actualNoFilter[0]).isEqualTo("A.FOO");
290         assertThat(actualNoFilter[1]).isEqualTo("B.BAR");
291 
292         final String[] actualFiltered = filteredDataSet.getTableNames();
293         assertThat(actualFiltered).hasSize(2);
294         assertThat(actualFiltered[0]).isEqualTo("A.FOO");
295         assertThat(actualFiltered[1]).isEqualTo("B.BAR");
296     }
297 }