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