001: /*
002:
003: Derby - Class org.apache.derbyTesting.functionTests.tests.jdbc4.TestResultSetMethods
004:
005: Licensed to the Apache Software Foundation (ASF) under one or more
006: contributor license agreements. See the NOTICE file distributed with
007: this work for additional information regarding copyright ownership.
008: The ASF licenses this file to you under the Apache License, Version 2.0
009: (the "License"); you may not use this file except in compliance with
010: the License. You may obtain a copy of the License at
011:
012: http://www.apache.org/licenses/LICENSE-2.0
013:
014: Unless required by applicable law or agreed to in writing, software
015: distributed under the License is distributed on an "AS IS" BASIS,
016: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: See the License for the specific language governing permissions and
018: limitations under the License.
019:
020: */
021:
022: package org.apache.derbyTesting.functionTests.tests.jdbc4;
023:
024: import org.apache.derby.impl.jdbc.Util;
025:
026: import java.io.Reader;
027: import java.io.InputStream;
028: import java.sql.Connection;
029: import java.sql.DriverManager;
030: import java.sql.PreparedStatement;
031: import java.sql.CallableStatement;
032: import java.sql.ResultSet;
033: import java.sql.SQLException;
034: import java.sql.Statement;
035: import org.apache.derby.shared.common.reference.SQLState;
036: import java.lang.reflect.InvocationTargetException;
037: import java.lang.reflect.Method;
038: import java.util.HashMap;
039: import org.apache.derby.tools.ij;
040: import org.apache.derbyTesting.functionTests.util.SQLStateConstants;
041:
042: /**
043: * This class is used to test the implementations of the JDBC 4.0 methods
044: * in the ResultSet interface
045: */
046: public class TestResultSetMethods {
047:
048: Connection conn = null;
049: PreparedStatement ps = null;
050: ResultSet rs = null;
051:
052: /**
053: * Checks that a <code>boolean</code> value is
054: * <code>true</code>. Throws an exception if it is false.
055: *
056: * @param expr boolean expected to be true
057: * @param msg message when assertion fails
058: * @exception RuntimeException if <code>expr</code> is false
059: */
060: private static void assert_(boolean expr, String msg) {
061: if (!expr) {
062: throw new RuntimeException("Assertion failed: " + msg);
063: }
064: }
065:
066: /**
067: * Tests that <code>ResultSet.getHoldability()</code> has the
068: * correct behaviour.
069: */
070: void t_getHoldability() {
071: Boolean savedAutoCommit = null;
072: try {
073: savedAutoCommit = conn.getAutoCommit();
074: conn.setAutoCommit(false);
075:
076: // test default holdability
077: Statement stmt = conn.createStatement();
078: ResultSet rs = stmt.executeQuery("values(1)");
079: assert_(
080: rs.getHoldability() == ResultSet.HOLD_CURSORS_OVER_COMMIT,
081: "default holdability is HOLD_CURSORS_OVER_COMMIT");
082: rs.close();
083: try {
084: rs.getHoldability();
085: assert_(false,
086: "getHoldability() should fail when closed");
087: } catch (SQLException sqle) {
088: String sqlState = sqle.getSQLState();
089: // client driver throws exception with SQL state null
090: // when result set is closed
091: if (sqlState != null && !sqlState.equals("XCL16")) {
092: throw sqle;
093: }
094: }
095:
096: // test explicitly set holdability
097: final int[] holdabilities = {
098: ResultSet.HOLD_CURSORS_OVER_COMMIT,
099: ResultSet.CLOSE_CURSORS_AT_COMMIT, };
100: for (int h : holdabilities) {
101: Statement s = conn.createStatement(
102: ResultSet.TYPE_FORWARD_ONLY,
103: ResultSet.CONCUR_READ_ONLY, h);
104: rs = s.executeQuery("values(1)");
105: assert_(rs.getHoldability() == h, "holdability "
106: + holdabilityString(h));
107: rs.close();
108: s.close();
109: }
110:
111: // test holdability of result set returned from a stored
112: // procedure (DERBY-1101)
113: stmt
114: .execute("create procedure getresultsetwithhold(in hold int) "
115: + "parameter style java language java external name "
116: + "'org.apache.derbyTesting.functionTests.tests."
117: + "jdbc4.TestResultSetMethods."
118: + "getResultSetWithHoldability' "
119: + "dynamic result sets 1 reads sql data");
120: for (int statementHoldability : holdabilities) {
121: for (int procHoldability : holdabilities) {
122: CallableStatement cs = conn.prepareCall(
123: "call getresultsetwithhold(?)",
124: ResultSet.TYPE_FORWARD_ONLY,
125: ResultSet.CONCUR_READ_ONLY,
126: statementHoldability);
127: cs.setInt(1, procHoldability);
128: cs.execute();
129: rs = cs.getResultSet();
130: int holdability = rs.getHoldability();
131: assert_(holdability == procHoldability,
132: "holdability of ResultSet from stored proc: "
133: + holdabilityString(holdability));
134: conn.commit();
135: boolean holdable;
136: try {
137: rs.next();
138: holdable = true;
139: } catch (SQLException sqle) {
140: String sqlstate = sqle.getSQLState();
141: // SQL state for closed result set is XCL16,
142: // but it is null in the client driver
143: if (sqlstate == null
144: || sqlstate.equals("XCL16")) {
145: holdable = false;
146: } else {
147: throw sqle;
148: }
149: }
150: if (holdable) {
151: assert_(
152: holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT,
153: "non-holdable result set not closed on commit");
154: } else {
155: assert_(
156: holdability == ResultSet.CLOSE_CURSORS_AT_COMMIT,
157: "holdable result set closed on commit");
158: }
159: rs.close();
160: cs.close();
161: }
162: }
163: stmt.execute("drop procedure getresultsetwithhold");
164: stmt.close();
165: conn.commit();
166: } catch (Exception e) {
167: System.out.println("Unexpected exception caught " + e);
168: e.printStackTrace(System.out);
169: } finally {
170: if (savedAutoCommit != null) {
171: try {
172: conn.setAutoCommit(savedAutoCommit);
173: } catch (SQLException sqle) {
174: sqle.printStackTrace(System.out);
175: }
176: }
177: }
178: }
179:
180: /**
181: * Convert holdability from an integer to a readable string.
182: *
183: * @param holdability an <code>int</code> value representing a holdability
184: * @return a <code>String</code> value representing the same holdability
185: */
186: private static String holdabilityString(int holdability) {
187: switch (holdability) {
188: case ResultSet.HOLD_CURSORS_OVER_COMMIT:
189: return "HOLD_CURSORS_OVER_COMMIT";
190: case ResultSet.CLOSE_CURSORS_AT_COMMIT:
191: return "CLOSE_CURSORS_AT_COMMIT";
192: default:
193: return "UNKNOWN HOLDABILITY";
194: }
195: }
196:
197: /**
198: * Tests that <code>ResultSet.isClosed()</code> returns the
199: * correct value in different situations.
200: */
201: void t_isClosed() {
202: try {
203: Statement stmt = conn.createStatement();
204:
205: // simple open/read/close test
206: ResultSet rs = stmt.executeQuery("values(1)");
207: assert_(!rs.isClosed(), "rs should be open");
208: while (rs.next())
209: ;
210: assert_(!rs.isClosed(), "rs should be open");
211: rs.close();
212: assert_(rs.isClosed(), "rs should be closed");
213:
214: // execute and re-execute statement
215: rs = stmt.executeQuery("values(1)");
216: assert_(!rs.isClosed(), "rs should be open");
217: ResultSet rs2 = stmt.executeQuery("values(1)");
218: assert_(rs.isClosed(), "rs should be closed");
219: assert_(!rs2.isClosed(), "rs2 should be open");
220:
221: // re-execute another statement on the same connection
222: Statement stmt2 = conn.createStatement();
223: rs = stmt2.executeQuery("values(1)");
224: assert_(!rs2.isClosed(), "rs2 should be open");
225: assert_(!rs.isClosed(), "rs should be open");
226:
227: // retrieve multiple result sets
228: stmt
229: .execute("create procedure retrieve_result_sets() "
230: + "parameter style java language java external name "
231: + "'org.apache.derbyTesting.functionTests.tests."
232: + "jdbc4.TestResultSetMethods.threeResultSets' "
233: + "dynamic result sets 3 reads sql data");
234: stmt.execute("call retrieve_result_sets()");
235: ResultSet[] rss = new ResultSet[3];
236: int count = 0;
237: do {
238: rss[count] = stmt.getResultSet();
239: assert_(!rss[count].isClosed(), "rss[" + count
240: + "] should be open");
241: if (count > 0) {
242: assert_(rss[count - 1].isClosed(), "rss["
243: + (count - 1) + "] should be closed");
244: }
245: ++count;
246: } while (stmt.getMoreResults());
247: assert_(count == 3, "expected three result sets");
248: stmt.execute("drop procedure retrieve_result_sets");
249:
250: // close statement
251: rs = stmt2.executeQuery("values(1)");
252: stmt2.close();
253: assert_(rs.isClosed(), "rs should be closed");
254:
255: // close connection
256: Connection conn2 = ij.startJBMS();
257: stmt2 = conn2.createStatement();
258: rs = stmt2.executeQuery("values(1)");
259: conn2.close();
260: assert_(rs.isClosed(), "rs should be closed");
261:
262: stmt.close();
263: stmt2.close();
264:
265: } catch (Exception e) {
266: System.out.println("Unexpected exception caught" + e);
267: e.printStackTrace();
268: }
269: }
270:
271: /**
272: * Test that an exception is thrown when methods are called
273: * on a closed result set (DERBY-1060).
274: */
275: private void testExceptionWhenClosed() {
276: try {
277: // create a result set and close it
278: Statement stmt = conn.createStatement();
279: ResultSet rs = stmt.executeQuery("values(1)");
280: rs.close();
281:
282: // maps method name to parameter list
283: HashMap<String, Class[]> params = new HashMap<String, Class[]>();
284: // maps method name to argument list
285: HashMap<String, Object[]> args = new HashMap<String, Object[]>();
286:
287: // methods with no parameters
288: String[] zeroArgMethods = { "getWarnings", "clearWarnings",
289: "getStatement", "getMetaData", "getConcurrency",
290: "getHoldability", "getRow", "getType",
291: "rowDeleted", "rowInserted", "rowUpdated",
292: "getFetchDirection", "getFetchSize", };
293: for (String name : zeroArgMethods) {
294: params.put(name, null);
295: args.put(name, null);
296: }
297:
298: // methods with a single int parameter
299: for (String name : new String[] { "setFetchDirection",
300: "setFetchSize" }) {
301: params.put(name, new Class[] { Integer.TYPE });
302: args.put(name, new Integer[] { 0 });
303: }
304:
305: // invoke the methods
306: for (String name : params.keySet()) {
307: try {
308: Method method = rs.getClass().getMethod(name,
309: params.get(name));
310: try {
311: method.invoke(rs, args.get(name));
312: } catch (InvocationTargetException ite) {
313: Throwable cause = ite.getCause();
314: if (cause instanceof SQLException) {
315: SQLException sqle = (SQLException) cause;
316: String state = sqle.getSQLState();
317: // Should get SQL state XCL16 when the
318: // result set is closed, but client driver
319: // sends null.
320: if (state == null || state.equals("XCL16")) {
321: continue;
322: }
323: }
324: throw cause;
325: }
326: System.out.println("no exception thrown for "
327: + name + "() when ResultSet is closed");
328: } catch (Throwable t) {
329: System.out.println("Unexpected exception when "
330: + "invoking " + name + "():");
331: t.printStackTrace(System.out);
332: }
333: }
334: stmt.close();
335: } catch (SQLException e) {
336: System.out.println("Unexpected exception caught:");
337: e.printStackTrace(System.out);
338: }
339: }
340:
341: /**
342: * Tests the wrapper methods isWrapperFor and unwrap. There are two cases
343: * to be tested
344: * Case 1: isWrapperFor returns true and we call unwrap
345: * Case 2: isWrapperFor returns false and we call unwrap
346: *
347: * @param rs The ResultSet object on which the wrapper
348: * methods are tested
349: */
350: void t_wrapper(ResultSet rs) {
351: Class<ResultSet> wrap_class = ResultSet.class;
352:
353: //The if succeeds and we call the unwrap method on the conn object
354: try {
355: if (rs.isWrapperFor(wrap_class)) {
356: ResultSet rs1 = (ResultSet) rs.unwrap(wrap_class);
357: } else {
358: System.out
359: .println("isWrapperFor wrongly returns false");
360: }
361: } catch (SQLException sqle) {
362: sqle.printStackTrace();
363: }
364:
365: //Being Test for Case2
366: //test for the case when isWrapper returns false
367: //using some class that will return false when
368: //passed to isWrapperFor
369: Class<PreparedStatement> wrap_class1 = PreparedStatement.class;
370:
371: try {
372: //returning false is the correct behaviour in this case
373: //Generate a message if it returns true
374: if (rs.isWrapperFor(wrap_class1)) {
375: System.out.println("isWrapperFor wrongly returns true");
376: } else {
377: PreparedStatement ps1 = (PreparedStatement) rs
378: .unwrap(wrap_class1);
379: System.out
380: .println("unwrap does not throw the expected "
381: + "exception");
382: }
383: } catch (SQLException sqle) {
384: //Calling unwrap in this case throws an
385: //SQLException ensure that this SQLException
386: //has the correct SQLState
387: if (!SQLStateConstants.UNABLE_TO_UNWRAP.equals(sqle
388: .getSQLState())) {
389: sqle.printStackTrace();
390: }
391: }
392: }
393:
394: void startTestResultSetMethods(Connection conn_in,
395: PreparedStatement ps_in, ResultSet rs_in) {
396: conn = conn_in;
397: ps = ps_in;
398: rs = rs_in;
399:
400: t_getHoldability();
401: t_isClosed();
402:
403: testExceptionWhenClosed();
404: }
405:
406: /**
407: * Method that is invoked by <code>t_isClosed()</code> (as a
408: * stored procedure) to retrieve three result sets.
409: *
410: * @param rs1 first result set
411: * @param rs2 second result set
412: * @param rs3 third result set
413: * @exception SQLException if a database error occurs
414: */
415: public static void threeResultSets(ResultSet[] rs1,
416: ResultSet[] rs2, ResultSet[] rs3) throws SQLException {
417: Connection c = DriverManager
418: .getConnection("jdbc:default:connection");
419: Statement stmt1 = c.createStatement();
420: rs1[0] = stmt1.executeQuery("values(1)");
421: Statement stmt2 = c.createStatement();
422: rs2[0] = stmt2.executeQuery("values(1)");
423: Statement stmt3 = c.createStatement();
424: rs3[0] = stmt3.executeQuery("values(1)");
425: c.close();
426: }
427:
428: /**
429: * Method invoked by <code>t_getHoldability()</code> (as a stored
430: * procedure) to retrieve a result set with a given holdability.
431: *
432: * @param holdability requested holdability
433: * @param rs result set returned from stored procedure
434: * @exception SQLException if a database error occurs
435: */
436: public static void getResultSetWithHoldability(int holdability,
437: ResultSet[] rs) throws SQLException {
438: Connection c = DriverManager
439: .getConnection("jdbc:default:connection");
440: Statement s = c.createStatement(ResultSet.TYPE_FORWARD_ONLY,
441: ResultSet.CONCUR_READ_ONLY, holdability);
442: rs[0] = s.executeQuery("values (1), (2), (3)");
443: c.close();
444: }
445:
446: /**
447: * <p>
448: * Return true if we're running under the embedded client.
449: * </p>
450: * @return a boolean value signifying whether we are running under the
451: * embedded framework
452: */
453: private static boolean usingEmbeddedClient() {
454: return "embedded".equals(System.getProperty("framework"));
455: }
456:
457: public static void main(String args[]) {
458: try {
459: // use the ij utility to read the property file and
460: // make the initial connection.
461: ij.getPropertyArg(args);
462:
463: Connection conn_main = ij.startJBMS();
464:
465: PreparedStatement ps_main = null;
466: ResultSet rs_main = null;
467:
468: ps_main = conn_main
469: .prepareStatement("select count(*) from sys.systables");
470: rs_main = ps_main.executeQuery();
471:
472: TestResultSetMethods trsm = new TestResultSetMethods();
473:
474: trsm.startTestResultSetMethods(conn_main, ps_main, rs_main);
475: trsm.t_wrapper(rs_main);
476: } catch (Exception e) {
477: System.out.println("" + e);
478: e.printStackTrace();
479: }
480:
481: }
482: }
|