View Javadoc
1   /*
2    *    Copyright 2009-2021 the original author or authors.
3    *
4    *    Licensed under the Apache License, Version 2.0 (the "License");
5    *    you may not use this file except in compliance with the License.
6    *    You may obtain a copy of the License at
7    *
8    *       http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *    Unless required by applicable law or agreed to in writing, software
11   *    distributed under the License is distributed on an "AS IS" BASIS,
12   *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *    See the License for the specific language governing permissions and
14   *    limitations under the License.
15   */
16  package org.apache.ibatis.jdbc;
17  
18  import static org.junit.jupiter.api.Assertions.*;
19  import static org.mockito.Mockito.*;
20  
21  import java.io.IOException;
22  import java.io.PrintWriter;
23  import java.io.Reader;
24  import java.io.StringReader;
25  import java.io.StringWriter;
26  import java.sql.Connection;
27  import java.sql.SQLException;
28  import java.sql.Statement;
29  import java.util.List;
30  import java.util.Map;
31  import java.util.Properties;
32  
33  import javax.sql.DataSource;
34  
35  import org.apache.ibatis.BaseDataTest;
36  import org.apache.ibatis.datasource.pooled.PooledDataSource;
37  import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
38  import org.apache.ibatis.io.Resources;
39  import org.junit.jupiter.api.Disabled;
40  import org.junit.jupiter.api.Test;
41  import org.mockito.Mockito;
42  
43  class ScriptRunnerTest extends BaseDataTest {
44  
45    private static final String LINE_SEPARATOR = System.lineSeparator();
46  
47    @Test
48    @Disabled("This fails with HSQLDB 2.0 due to the create index statements in the schema script")
49    void shouldRunScriptsBySendingFullScriptAtOnce() throws Exception {
50      DataSource ds = createUnpooledDataSource(JPETSTORE_PROPERTIES);
51      Connection conn = ds.getConnection();
52      ScriptRunner runner = new ScriptRunner(conn);
53      runner.setSendFullScript(true);
54      runner.setAutoCommit(true);
55      runner.setStopOnError(false);
56      runner.setErrorLogWriter(null);
57      runner.setLogWriter(null);
58      conn.close();
59      runJPetStoreScripts(runner);
60      assertProductsTableExistsAndLoaded();
61    }
62  
63    @Test
64    void shouldRunScriptsUsingConnection() throws Exception {
65      DataSource ds = createUnpooledDataSource(JPETSTORE_PROPERTIES);
66      try (Connection conn = ds.getConnection()) {
67        ScriptRunner runner = new ScriptRunner(conn);
68        runner.setAutoCommit(true);
69        runner.setStopOnError(false);
70        runner.setErrorLogWriter(null);
71        runner.setLogWriter(null);
72        runJPetStoreScripts(runner);
73      }
74      assertProductsTableExistsAndLoaded();
75    }
76  
77    @Test
78    void shouldRunScriptsUsingProperties() throws Exception {
79      Properties props = Resources.getResourceAsProperties(JPETSTORE_PROPERTIES);
80      DataSource dataSource = new UnpooledDataSource(
81          props.getProperty("driver"),
82          props.getProperty("url"),
83          props.getProperty("username"),
84          props.getProperty("password"));
85      ScriptRunner runner = new ScriptRunner(dataSource.getConnection());
86      runner.setAutoCommit(true);
87      runner.setStopOnError(false);
88      runner.setErrorLogWriter(null);
89      runner.setLogWriter(null);
90      runJPetStoreScripts(runner);
91      assertProductsTableExistsAndLoaded();
92    }
93  
94    @Test
95    void shouldReturnWarningIfEndOfLineTerminatorNotFound() throws Exception {
96      DataSource ds = createUnpooledDataSource(JPETSTORE_PROPERTIES);
97      String resource = "org/apache/ibatis/jdbc/ScriptMissingEOLTerminator.sql";
98      try (Connection conn = ds.getConnection();
99           Reader reader = Resources.getResourceAsReader(resource)) {
100       ScriptRunner runner = new ScriptRunner(conn);
101       runner.setAutoCommit(true);
102       runner.setStopOnError(false);
103       runner.setErrorLogWriter(null);
104       runner.setLogWriter(null);
105 
106       try {
107         runner.runScript(reader);
108         fail("Expected script runner to fail due to missing end of line terminator.");
109       } catch (Exception e) {
110         assertTrue(e.getMessage().contains("end-of-line terminator"));
111       }
112     }
113   }
114 
115   @Test
116   void commentAferStatementDelimiterShouldNotCauseRunnerFail() throws Exception {
117     DataSource ds = createUnpooledDataSource(JPETSTORE_PROPERTIES);
118     String resource = "org/apache/ibatis/jdbc/ScriptCommentAfterEOLTerminator.sql";
119     try (Connection conn = ds.getConnection();
120          Reader reader = Resources.getResourceAsReader(resource)) {
121       ScriptRunner runner = new ScriptRunner(conn);
122       runner.setAutoCommit(true);
123       runner.setStopOnError(true);
124       runner.setErrorLogWriter(null);
125       runner.setLogWriter(null);
126       runJPetStoreScripts(runner);
127       runner.runScript(reader);
128     }
129   }
130 
131   @Test
132   void shouldReturnWarningIfNotTheCurrentDelimiterUsed() throws Exception {
133     DataSource ds = createUnpooledDataSource(JPETSTORE_PROPERTIES);
134     String resource = "org/apache/ibatis/jdbc/ScriptChangingDelimiterMissingDelimiter.sql";
135     try (Connection conn = ds.getConnection();
136          Reader reader = Resources.getResourceAsReader(resource)) {
137       ScriptRunner runner = new ScriptRunner(conn);
138       runner.setAutoCommit(false);
139       runner.setStopOnError(true);
140       runner.setErrorLogWriter(null);
141       runner.setLogWriter(null);
142       try {
143         runner.runScript(reader);
144         fail("Expected script runner to fail due to the usage of invalid delimiter.");
145       } catch (Exception e) {
146         assertTrue(e.getMessage().contains("end-of-line terminator"));
147       }
148     }
149   }
150 
151   @Test
152   void changingDelimiterShouldNotCauseRunnerFail() throws Exception {
153     DataSource ds = createUnpooledDataSource(JPETSTORE_PROPERTIES);
154     String resource = "org/apache/ibatis/jdbc/ScriptChangingDelimiter.sql";
155     try (Connection conn = ds.getConnection();
156          Reader reader = Resources.getResourceAsReader(resource)) {
157       ScriptRunner runner = new ScriptRunner(conn);
158       runner.setAutoCommit(false);
159       runner.setStopOnError(true);
160       runner.setErrorLogWriter(null);
161       runner.setLogWriter(null);
162       runJPetStoreScripts(runner);
163       runner.runScript(reader);
164     }
165   }
166 
167   @Test
168   void testLogging() throws Exception {
169     DataSource ds = createUnpooledDataSource(JPETSTORE_PROPERTIES);
170     try (Connection conn = ds.getConnection()) {
171       ScriptRunner runner = new ScriptRunner(conn);
172       runner.setAutoCommit(true);
173       runner.setStopOnError(false);
174       runner.setErrorLogWriter(null);
175       runner.setSendFullScript(false);
176       StringWriter sw = new StringWriter();
177       PrintWriter logWriter = new PrintWriter(sw);
178       runner.setLogWriter(logWriter);
179 
180       Reader reader = new StringReader("select userid from account where userid = 'j2ee';");
181       runner.runScript(reader);
182 
183       assertEquals(
184               "select userid from account where userid = 'j2ee'" + LINE_SEPARATOR
185                       + LINE_SEPARATOR + "USERID\t" + LINE_SEPARATOR
186                       + "j2ee\t" + LINE_SEPARATOR, sw.toString());
187     }
188   }
189 
190   @Test
191   void testLoggingFullScipt() throws Exception {
192     DataSource ds = createUnpooledDataSource(JPETSTORE_PROPERTIES);
193     try (Connection conn = ds.getConnection()) {
194       ScriptRunner runner = new ScriptRunner(conn);
195       runner.setAutoCommit(true);
196       runner.setStopOnError(false);
197       runner.setErrorLogWriter(null);
198       runner.setSendFullScript(true);
199       StringWriter sw = new StringWriter();
200       PrintWriter logWriter = new PrintWriter(sw);
201       runner.setLogWriter(logWriter);
202 
203       Reader reader = new StringReader("select userid from account where userid = 'j2ee';");
204       runner.runScript(reader);
205 
206       assertEquals(
207               "select userid from account where userid = 'j2ee';" + LINE_SEPARATOR
208                       + LINE_SEPARATOR + "USERID\t" + LINE_SEPARATOR
209                       + "j2ee\t" + LINE_SEPARATOR, sw.toString());
210     }
211   }
212 
213   private void runJPetStoreScripts(ScriptRunner runner) throws IOException, SQLException {
214     runScript(runner, JPETSTORE_DDL);
215     runScript(runner, JPETSTORE_DATA);
216   }
217 
218   private void assertProductsTableExistsAndLoaded() throws IOException, SQLException {
219     PooledDataSource ds = createPooledDataSource(JPETSTORE_PROPERTIES);
220     try (Connection conn = ds.getConnection()) {
221       SqlRunner executor = new SqlRunner(conn);
222       List<Map<String, Object>> products = executor.selectAll("SELECT * FROM PRODUCT");
223       assertEquals(16, products.size());
224     } finally {
225       ds.forceCloseAll();
226     }
227   }
228 
229   @Test
230   void shouldAcceptDelimiterVariations() throws Exception {
231     Connection conn = mock(Connection.class);
232     Statement stmt = mock(Statement.class);
233     when(conn.createStatement()).thenReturn(stmt);
234     when(stmt.getUpdateCount()).thenReturn(-1);
235     ScriptRunner runner = new ScriptRunner(conn);
236 
237     String sql = "-- @DELIMITER | \n"
238         + "line 1;\n"
239         + "line 2;\n"
240         + "|\n"
241         + "//  @DELIMITER  ;\n"
242         + "line 3; \n"
243         + "-- //@deLimiTer $  blah\n"
244         + "line 4$\n"
245         + "// //@DELIMITER %\n"
246         + "line 5%\n";
247     Reader reader = new StringReader(sql);
248     runner.runScript(reader);
249 
250     verify(stmt, Mockito.times(1)).execute(eq("line 1;" + LINE_SEPARATOR + "line 2;" + LINE_SEPARATOR + LINE_SEPARATOR));
251     verify(stmt, Mockito.times(1)).execute(eq("line 3" + LINE_SEPARATOR));
252     verify(stmt, Mockito.times(1)).execute(eq("line 4" + LINE_SEPARATOR));
253     verify(stmt, Mockito.times(1)).execute(eq("line 5" + LINE_SEPARATOR));
254   }
255 
256   @Test
257   void test() {
258     StringBuilder sb = new StringBuilder();
259     StringBuilder sb2 = y(sb);
260     assertSame(sb, sb2);
261   }
262 
263   private StringBuilder y(StringBuilder sb) {
264     sb.append("ABC");
265     return sb;
266   }
267 
268   @Test
269   void shouldAcceptMultiCharDelimiter() throws Exception {
270     Connection conn = mock(Connection.class);
271     Statement stmt = mock(Statement.class);
272     when(conn.createStatement()).thenReturn(stmt);
273     when(stmt.getUpdateCount()).thenReturn(-1);
274     ScriptRunner runner = new ScriptRunner(conn);
275 
276     String sql = "-- @DELIMITER || \n"
277         + "line 1;\n"
278         + "line 2;\n"
279         + "||\n"
280         + "//  @DELIMITER  ;\n"
281         + "line 3; \n";
282     Reader reader = new StringReader(sql);
283     runner.runScript(reader);
284 
285     verify(stmt, Mockito.times(1)).execute(eq("line 1;" + LINE_SEPARATOR + "line 2;" + LINE_SEPARATOR + LINE_SEPARATOR));
286     verify(stmt, Mockito.times(1)).execute(eq("line 3" + LINE_SEPARATOR));
287   }
288 }