1 | /* |
2 | |
3 | Derby - Class org.apache.derby.impl.tools.ij.utilMain |
4 | |
5 | Copyright 1997, 2004 The Apache Software Foundation or its licensors, as applicable. |
6 | |
7 | Licensed under the Apache License, Version 2.0 (the "License"); |
8 | you may not use this file except in compliance with the License. |
9 | You may obtain a copy of the License at |
10 | |
11 | http://www.apache.org/licenses/LICENSE-2.0 |
12 | |
13 | Unless required by applicable law or agreed to in writing, software |
14 | distributed under the License is distributed on an "AS IS" BASIS, |
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | See the License for the specific language governing permissions and |
17 | limitations under the License. |
18 | |
19 | */ |
20 | |
21 | package org.apache.derby.impl.tools.ij; |
22 | |
23 | import org.apache.derby.iapi.reference.JDBC20Translation; |
24 | import org.apache.derby.iapi.reference.JDBC30Translation; |
25 | |
26 | import org.apache.derby.tools.JDBCDisplayUtil; |
27 | import org.apache.derby.iapi.tools.i18n.*; |
28 | |
29 | import org.apache.derby.iapi.services.info.ProductVersionHolder; |
30 | import org.apache.derby.iapi.services.info.ProductGenusNames; |
31 | |
32 | import org.apache.derby.iapi.error.PublicAPI; |
33 | import org.apache.derby.iapi.error.StandardException; |
34 | |
35 | import java.util.Stack; |
36 | import java.util.Hashtable; |
37 | import java.util.Properties; |
38 | |
39 | import java.io.InputStream; |
40 | import java.io.FileInputStream; |
41 | import java.io.BufferedInputStream; |
42 | import java.io.FileNotFoundException; |
43 | import java.io.StringReader; |
44 | import java.sql.DriverManager; |
45 | import java.sql.Driver; |
46 | import java.sql.Connection; |
47 | import java.sql.SQLException; |
48 | import java.sql.ResultSet; |
49 | import java.sql.Statement; |
50 | import java.sql.PreparedStatement; |
51 | |
52 | import java.lang.reflect.*; |
53 | |
54 | /** |
55 | This class is utilities specific to the two ij Main's. |
56 | This factoring enables sharing the functionality for |
57 | single and dual connection ij runs. |
58 | |
59 | @author jerry |
60 | */ |
61 | public class utilMain implements java.security.PrivilegedAction { |
62 | |
63 | private static Class[] CONN_PARAM = { Integer.TYPE }; |
64 | private static Object[] CONN_ARG = { new Integer(JDBC30Translation.CLOSE_CURSORS_AT_COMMIT)}; |
65 | |
66 | StatementFinder[] commandGrabber; |
67 | UCode_CharStream charStream; |
68 | ijTokenManager ijTokMgr; |
69 | ij ijParser; |
70 | ConnectionEnv[] connEnv; |
71 | int currCE; |
72 | private int numConnections; |
73 | private boolean fileInput; |
74 | private boolean initialFileInput; |
75 | private boolean mtUse; |
76 | private boolean firstRun = true; |
77 | private LocalizedOutput out = null; |
78 | private Properties connAttributeDefaults; |
79 | private Hashtable ignoreErrors; |
80 | |
81 | protected boolean isJCC; //The driver being used is JCC |
82 | |
83 | /* |
84 | In the goodness of time, this could be an ij property |
85 | */ |
86 | public static final int BUFFEREDFILESIZE = 2048; |
87 | |
88 | /* |
89 | * command can be redirected, so we stack up command |
90 | * grabbers as needed. |
91 | */ |
92 | Stack oldGrabbers = new Stack(); |
93 | |
94 | LocalizedResource langUtil = LocalizedResource.getInstance(); |
95 | /** |
96 | * Set up the test to run with 'numConnections' connections/users. |
97 | * |
98 | * @param numConnections The number of connections/users to test. |
99 | */ |
100 | public utilMain(int numConnections, LocalizedOutput out) |
101 | throws ijFatalException |
102 | { |
103 | this(numConnections, out, (Hashtable)null); |
104 | } |
105 | |
106 | /** |
107 | * Set up the test to run with 'numConnections' connections/users. |
108 | * |
109 | * @param numConnections The number of connections/users to test. |
110 | * @param ignoreErrors A list of errors to ignore. If null, |
111 | * all errors are printed out and nothing |
112 | * is fatal. If non-null, if an error is |
113 | * hit and it is in this list, it is silently |
114 | * ignore. Otherwise, an ijFatalException is |
115 | * thrown. ignoreErrors is used for stress |
116 | * tests. |
117 | */ |
118 | public utilMain(int numConnections, LocalizedOutput out, Hashtable ignoreErrors) |
119 | throws ijFatalException |
120 | { |
121 | String framework_property = util.getSystemProperty("framework"); |
122 | |
123 | if (framework_property != null) |
124 | { |
125 | if (framework_property.equals("DB2jNet") |
126 | || framework_property.equals("DB2jcc")) |
127 | isJCC = true; |
128 | } |
129 | /* init the parser; give it no input to start with. |
130 | * (1 parser for entire test.) |
131 | */ |
132 | charStream = new UCode_CharStream( |
133 | new StringReader(" "), 1, 1); |
134 | ijTokMgr = new ijTokenManager(charStream); |
135 | ijParser = new ij(ijTokMgr, getUtilMain()); |
136 | this.out = out; |
137 | this.ignoreErrors = ignoreErrors; |
138 | |
139 | this.numConnections = numConnections; |
140 | /* 1 StatementFinder and ConnectionEnv per connection/user. */ |
141 | commandGrabber = new StatementFinder[numConnections]; |
142 | connEnv = new ConnectionEnv[numConnections]; |
143 | |
144 | for (int ictr = 0; ictr < numConnections; ictr++) |
145 | { |
146 | commandGrabber[ictr] = new StatementFinder(langUtil.getNewInput(System.in)); |
147 | connEnv[ictr] = new ConnectionEnv(ictr, (numConnections > 1), (numConnections == 1)); |
148 | try { |
149 | connEnv[ictr].init(out); |
150 | } catch (SQLException s) { |
151 | JDBCDisplayUtil.ShowException(out, s); // will continue past connect failure |
152 | } catch (ClassNotFoundException c) { |
153 | JDBCDisplayUtil.ShowException(out, c); // will continue past driver failure |
154 | } catch (InstantiationException i) { |
155 | JDBCDisplayUtil.ShowException(out, i); // will continue past driver failure |
156 | } catch (IllegalAccessException ia) { |
157 | JDBCDisplayUtil.ShowException(out, ia); // will continue past driver failure |
158 | } |
159 | } |
160 | |
161 | /* Start with connection/user 0 */ |
162 | currCE = 0; |
163 | fileInput = false; |
164 | initialFileInput = false; |
165 | firstRun = true; |
166 | } |
167 | |
168 | |
169 | /** |
170 | * run ij over the specified input, sending output to the |
171 | * specified output. Any prior input and output will be lost. |
172 | * |
173 | * @param in source for input to ij |
174 | * @param out sink for output from ij |
175 | * @param connAttributeDefaults connection attributes from -ca ij arg |
176 | */ |
177 | public void go(LocalizedInput[] in, LocalizedOutput out, |
178 | Properties connAttributeDefaults) throws ijFatalException |
179 | { |
180 | boolean done = false; |
181 | |
182 | String command = null; |
183 | |
184 | this.out = out; |
185 | this.connAttributeDefaults = connAttributeDefaults; |
186 | |
187 | ijParser.setConnection(connEnv[currCE], (numConnections > 1)); |
188 | fileInput = initialFileInput = (!in[currCE].isStandardInput()); |
189 | |
190 | for (int ictr = 0; ictr < commandGrabber.length; ictr++) { |
191 | commandGrabber[ictr].ReInit(in[ictr]); |
192 | } |
193 | |
194 | if (firstRun) { |
195 | |
196 | // figure out which version this is |
197 | InputStream versionStream = (InputStream) java.security.AccessController.doPrivileged(this); |
198 | |
199 | // figure out which version this is |
200 | ProductVersionHolder ijVersion = |
201 | ProductVersionHolder.getProductVersionHolderFromMyEnv(versionStream); |
202 | |
203 | String version; |
204 | if (ijVersion != null) |
205 | { |
206 | version = "" + ijVersion.getMajorVersion() + "." + |
207 | ijVersion.getMinorVersion(); |
208 | } |
209 | else |
210 | { |
211 | version = "?"; |
212 | } |
213 | |
214 | out.println(langUtil.getTextMessage("IJ_IjVers30C199", version)); |
215 | for (int i=connEnv.length-1;i>=0;i--) { // print out any initial warnings... |
216 | Connection c = connEnv[i].getConnection(); |
217 | if (c!=null) { |
218 | JDBCDisplayUtil.ShowWarnings(out,c); |
219 | } |
220 | } |
221 | firstRun = false; |
222 | |
223 | //check if the property is set to not show select count and set the static variable |
224 | //accordingly. |
225 | boolean showNoCountForSelect = Boolean.getBoolean("ij.showNoCountForSelect"); |
226 | JDBCDisplayUtil.showSelectCount = !showNoCountForSelect; |
227 | |
228 | //check if the property is set to not show initial connections and accordingly set the |
229 | //static variable. |
230 | boolean showNoConnectionsAtStart = Boolean.getBoolean("ij.showNoConnectionsAtStart"); |
231 | if (!(showNoConnectionsAtStart)) { |
232 | try { |
233 | ijResult result = ijParser.showConnectionsMethod(true); |
234 | displayResult(out,result,connEnv[currCE].getConnection()); |
235 | } catch (SQLException ex) { |
236 | handleSQLException(out,ex); |
237 | } |
238 | } |
239 | } |
240 | |
241 | while (!ijParser.exit && !done) { |
242 | try{ |
243 | ijParser.setConnection(connEnv[currCE], (numConnections > 1)); |
244 | } catch(Throwable t){ |
245 | //do nothing |
246 | } |
247 | |
248 | connEnv[currCE].doPrompt(true, out); |
249 | try { |
250 | command = null; |
251 | |
252 | out.flush(); |
253 | command = commandGrabber[currCE].nextStatement(); |
254 | |
255 | // if there is no next statement, |
256 | // pop back to the top saved grabber. |
257 | while (command == null && ! oldGrabbers.empty()) { |
258 | // close the old input file if not System.in |
259 | if (fileInput) commandGrabber[currCE].close(); |
260 | commandGrabber[currCE] = (StatementFinder)oldGrabbers.pop(); |
261 | if (oldGrabbers.empty()) |
262 | fileInput = initialFileInput; |
263 | command = commandGrabber[currCE].nextStatement(); |
264 | } |
265 | |
266 | // if there are no grabbers left, |
267 | // we are done. |
268 | if (command == null && oldGrabbers.empty()) { |
269 | done = true; |
270 | } |
271 | else { |
272 | boolean elapsedTimeOn = ijParser.getElapsedTimeState(); |
273 | long beginTime = 0; |
274 | long endTime; |
275 | |
276 | if (fileInput) { |
277 | out.println(command+";"); |
278 | out.flush(); |
279 | } |
280 | |
281 | charStream.ReInit(new StringReader(command), 1, 1); |
282 | ijTokMgr.ReInit(charStream); |
283 | ijParser.ReInit(ijTokMgr); |
284 | |
285 | if (elapsedTimeOn) { |
286 | beginTime = System.currentTimeMillis(); |
287 | } |
288 | |
289 | ijResult result = ijParser.ijStatement(); |
290 | displayResult(out,result,connEnv[currCE].getConnection()); |
291 | |
292 | // if something went wrong, an SQLException or ijException was thrown. |
293 | // we can keep going to the next statement on those (see catches below). |
294 | // ijParseException means we try the SQL parser. |
295 | |
296 | /* Print the elapsed time if appropriate */ |
297 | if (elapsedTimeOn) { |
298 | endTime = System.currentTimeMillis(); |
299 | out.println(langUtil.getTextMessage("IJ_ElapTime0Mil", |
300 | langUtil.getNumberAsString(endTime - beginTime))); |
301 | } |
302 | |
303 | // would like when it completes a statement |
304 | // to see if there is stuff after the ; |
305 | // and before the <EOL> that we will IGNORE |
306 | // (with a warning to that effect) |
307 | } |
308 | |
309 | } catch (ParseException e) { |
310 | if (command != null) doCatch(command); |
311 | } catch (TokenMgrError e) { |
312 | if (command != null) doCatch(command); |
313 | } catch (SQLException e) { |
314 | // SQL exception occurred in ij's actions; print and continue |
315 | // unless it is considered fatal. |
316 | handleSQLException(out,e); |
317 | } catch (ijException e) { |
318 | // exception occurred in ij's actions; print and continue |
319 | out.println(langUtil.getTextMessage("IJ_IjErro0",e.getMessage())); |
320 | doTrace(e); |
321 | } catch (Throwable e) { |
322 | out.println(langUtil.getTextMessage("IJ_JavaErro0",e.toString())); |
323 | doTrace(e); |
324 | } |
325 | |
326 | /* Go to the next connection/user, if there is one */ |
327 | currCE = ++currCE % connEnv.length; |
328 | } |
329 | |
330 | // we need to close all sessions when done; otherwise we have |
331 | // a problem when a single VM runs successive IJ threads |
332 | try { |
333 | for (int i = 0; i < connEnv.length; i++) { |
334 | connEnv[i].removeAllSessions(); |
335 | } |
336 | } catch (SQLException se ) { |
337 | handleSQLException(out,se); |
338 | } |
339 | // similarly must close input files |
340 | for (int i = 0; i < numConnections; i++) { |
341 | try { |
342 | if (!in[i].isStandardInput() ) |
343 | in[i].close(); |
344 | } catch (Exception e ) { |
345 | out.println(langUtil.getTextMessage("IJ_CannotCloseInFile", |
346 | e.toString())); |
347 | } |
348 | } |
349 | |
350 | /* |
351 | If an exit was requested, then we will be shutting down. |
352 | */ |
353 | if (ijParser.exit || (initialFileInput && !mtUse)) { |
354 | Driver d = null; |
355 | try { |
356 | d = DriverManager.getDriver("jdbc:derby:"); |
357 | } catch (Throwable e) { |
358 | d = null; |
359 | } |
360 | if (d!=null) { // do we have a driver running? shutdown on exit. |
361 | try { |
362 | DriverManager.getConnection("jdbc:derby:;shutdown=true"); |
363 | } catch (SQLException e) { |
364 | // ignore the errors, they are expected. |
365 | } |
366 | } |
367 | } |
368 | } |
369 | |
370 | private void displayResult(LocalizedOutput out, ijResult result, Connection conn) throws SQLException { |
371 | // display the result, if appropriate. |
372 | if (result!=null) { |
373 | if (result.isConnection()) { |
374 | if (result.hasWarnings()) { |
375 | JDBCDisplayUtil.ShowWarnings(out,result.getSQLWarnings()); |
376 | result.clearSQLWarnings(); |
377 | } |
378 | } else if (result.isStatement()) { |
379 | Statement s = result.getStatement(); |
380 | try { |
381 | JDBCDisplayUtil.DisplayResults(out,s,connEnv[currCE].getConnection()); |
382 | } catch (SQLException se) { |
383 | result.closeStatement(); |
384 | throw se; |
385 | } |
386 | result.closeStatement(); |
387 | } else if (result.isNextRowOfResultSet()) { |
388 | ResultSet r = result.getNextRowOfResultSet(); |
389 | JDBCDisplayUtil.DisplayCurrentRow(out,r,connEnv[currCE].getConnection()); |
390 | } else if (result.isVector()) { |
391 | util.DisplayVector(out,result.getVector()); |
392 | if (result.hasWarnings()) { |
393 | JDBCDisplayUtil.ShowWarnings(out,result.getSQLWarnings()); |
394 | result.clearSQLWarnings(); |
395 | } |
396 | } else if (result.isMulti()) { |
397 | try { |
398 | util.DisplayMulti(out,(PreparedStatement)result.getStatement(),result.getResultSet(),connEnv[currCE].getConnection()); |
399 | } catch (SQLException se) { |
400 | result.closeStatement(); |
401 | throw se; |
402 | } |
403 | result.closeStatement(); // done with the statement now |
404 | if (result.hasWarnings()) { |
405 | JDBCDisplayUtil.ShowWarnings(out,result.getSQLWarnings()); |
406 | result.clearSQLWarnings(); |
407 | } |
408 | } else if (result.isException()) { |
409 | JDBCDisplayUtil.ShowException(out,result.getException()); |
410 | } |
411 | } |
412 | } |
413 | |
414 | /** |
415 | * catch processing on failed commands. This really ought to |
416 | * be in ij somehow, but it was easier to catch in Main. |
417 | */ |
418 | private void doCatch(String command) { |
419 | // this retries the failed statement |
420 | // as a JSQL statement; it uses the |
421 | // ijParser since that maintains our |
422 | // connection and state. |
423 | |
424 | try { |
425 | boolean elapsedTimeOn = ijParser.getElapsedTimeState(); |
426 | long beginTime = 0; |
427 | long endTime; |
428 | |
429 | if (elapsedTimeOn) { |
430 | beginTime = System.currentTimeMillis(); |
431 | } |
432 | |
433 | ijResult result = ijParser.executeImmediate(command); |
434 | displayResult(out,result,connEnv[currCE].getConnection()); |
435 | |
436 | /* Print the elapsed time if appropriate */ |
437 | if (elapsedTimeOn) { |
438 | endTime = System.currentTimeMillis(); |
439 | out.println(langUtil.getTextMessage("IJ_ElapTime0Mil_4", |
440 | langUtil.getNumberAsString(endTime - beginTime))); |
441 | } |
442 | |
443 | } catch (SQLException e) { |
444 | // SQL exception occurred in ij's actions; print and continue |
445 | // unless it is considered fatal. |
446 | handleSQLException(out,e); |
447 | } catch (ijException i) { |
448 | out.println(langUtil.getTextMessage("IJ_IjErro0_5", i.getMessage())); |
449 | doTrace(i); |
450 | } catch (ijTokenException ie) { |
451 | out.println(langUtil.getTextMessage("IJ_IjErro0_6", ie.getMessage())); |
452 | doTrace(ie); |
453 | } catch (Throwable t) { |
454 | out.println(langUtil.getTextMessage("IJ_JavaErro0_7", t.toString())); |
455 | doTrace(t); |
456 | } |
457 | } |
458 | |
459 | /** |
460 | * This routine displays SQL exceptions and decides whether they |
461 | * are fatal or not, based on the ignoreErrors field. If they |
462 | * are fatal, an ijFatalException is thrown. |
463 | * Lifted from ij/util.java:ShowSQLException |
464 | */ |
465 | public void handleSQLException(LocalizedOutput out, SQLException e) |
466 | throws ijFatalException |
467 | { |
468 | String errorCode; |
469 | String sqlState = null; |
470 | SQLException fatalException = null; |
471 | |
472 | if (Boolean.getBoolean("ij.showErrorCode")) { |
473 | errorCode = langUtil.getTextMessage("IJ_Erro0", |
474 | langUtil.getNumberAsString(e.getErrorCode())); |
475 | } |
476 | else { |
477 | errorCode = ""; |
478 | } |
479 | |
480 | for (; e!=null; e=e.getNextException()) |
481 | { |
482 | /* |
483 | ** If we are to throw errors, then throw the exceptions |
484 | ** that aren't in the ignoreErrors list. If |
485 | ** the ignoreErrors list is null we don't throw |
486 | ** any errors. |
487 | */ |
488 | if (ignoreErrors != null) |
489 | { |
490 | sqlState = e.getSQLState(); |
491 | if ((sqlState != null) && |
492 | (ignoreErrors.get(sqlState) != null)) |
493 | { |
494 | continue; |
495 | } |
496 | else |
497 | { |
498 | fatalException = e; |
499 | } |
500 | } |
501 | |
502 | String st1 = JDBCDisplayUtil.mapNull(e.getSQLState(),langUtil.getTextMessage("IJ_NoSqls")); |
503 | String st2 = JDBCDisplayUtil.mapNull(e.getMessage(),langUtil.getTextMessage("IJ_NoMess")); |
504 | out.println(langUtil.getTextMessage("IJ_Erro012", st1, st2, errorCode)); |
505 | JDBCDisplayUtil.doTrace(out, e); |
506 | } |
507 | if (fatalException != null) |
508 | { |
509 | throw new ijFatalException(fatalException); |
510 | } |
511 | } |
512 | |
513 | /** |
514 | * stack trace dumper |
515 | */ |
516 | private void doTrace(Throwable t) { |
517 | if (util.getSystemProperty("ij.exceptionTrace") != null) { |
518 | t.printStackTrace(out); |
519 | } |
520 | out.flush(); |
521 | } |
522 | |
523 | void newInput(String fileName) { |
524 | FileInputStream newFile = null; |
525 | try { |
526 | newFile = new FileInputStream(fileName); |
527 | } catch (FileNotFoundException e) { |
528 | throw ijException.fileNotFound(); |
529 | } |
530 | if (newFile == null) return; |
531 | |
532 | // if the file was opened, move to use it for input. |
533 | oldGrabbers.push(commandGrabber[currCE]); |
534 | commandGrabber[currCE] = |
535 | new StatementFinder(langUtil.getNewInput(new BufferedInputStream(newFile, BUFFEREDFILESIZE))); |
536 | fileInput = true; |
537 | } |
538 | |
539 | void newResourceInput(String resourceName) { |
540 | InputStream is = util.getResourceAsStream(resourceName); |
541 | if (is==null) throw ijException.resourceNotFound(); |
542 | oldGrabbers.push(commandGrabber[currCE]); |
543 | commandGrabber[currCE] = |
544 | new StatementFinder(langUtil.getNewEncodedInput(new BufferedInputStream(is, BUFFEREDFILESIZE), "UTF8")); |
545 | fileInput = true; |
546 | } |
547 | |
548 | /** |
549 | * REMIND: eventually this might be part of StatementFinder, |
550 | * used at each carriage return to show that it is still "live" |
551 | * when it is reading multi-line input. |
552 | */ |
553 | static void doPrompt(boolean newStatement, LocalizedOutput out, String tag) |
554 | { |
555 | if (newStatement) { |
556 | out.print("ij"+(tag==null?"":tag)+"> "); |
557 | } |
558 | else { |
559 | out.print("> "); |
560 | } |
561 | out.flush(); |
562 | } |
563 | |
564 | void setMtUse(boolean b) { |
565 | mtUse = b; |
566 | } |
567 | |
568 | // JDBC 2.0 support |
569 | |
570 | /** |
571 | * Return the right utilMain to use. (JDBC 1.1 or 2.0) |
572 | * |
573 | */ |
574 | public utilMain getUtilMain() |
575 | { |
576 | return this; |
577 | } |
578 | |
579 | /** |
580 | * Connections by default create ResultSet objects with holdability true. This method can be used |
581 | * to change the holdability of the connection by passing one of ResultSet.HOLD_CURSORS_OVER_COMMIT |
582 | * or ResultSet.CLOSE_CURSORS_AT_COMMIT. We implement this using reflection in jdk13 and lower |
583 | * |
584 | * @param conn The connection. |
585 | * @param holdType The new holdability for the Connection object. |
586 | * |
587 | * @return The connection object with holdability set to passed value. |
588 | */ |
589 | public Connection setHoldability(Connection conn, int holdType) |
590 | throws SQLException |
591 | { |
592 | //Prior to db2 compatibility work, the default holdability for connections was close cursors over commit and all the tests |
593 | //were written based on that assumption |
594 | //Later, as part of db2 compatibility, we changed the default holdability for connection to hold cursors over commit. |
595 | //But in order for the existing tests to work fine, the tests needed a way to set the holdability to close cursors for connections |
596 | //Since there is no direct jdbc api in jdk13 and lower to do that, we are using reflection to set the holdability to close cursors |
597 | try { //for jdks prior to jdk14, need to use reflection to set holdability to false. |
598 | Method sh = conn.getClass().getMethod("setHoldability", CONN_PARAM); |
599 | sh.invoke(conn, CONN_ARG); |
600 | } catch( Exception e) { |
601 | throw PublicAPI.wrapStandardException( StandardException.plainWrapException( e)); |
602 | } |
603 | return conn; |
604 | } |
605 | |
606 | /** |
607 | * Retrieves the current holdability of ResultSet objects created using this |
608 | * Connection object. We implement this using reflection in jdk13 and lower |
609 | * |
610 | * @return The holdability, one of ResultSet.HOLD_CURSORS_OVER_COMMIT |
611 | * or ResultSet.CLOSE_CURSORS_AT_COMMIT |
612 | * |
613 | */ |
614 | public int getHoldability(Connection conn) |
615 | throws SQLException |
616 | { |
617 | //this method is used to make sure we are not trying to create a statement with holdability different than the connection holdability |
618 | //This is because jdk13 and lower does not have support for that. |
619 | //The holdability of connection and statement can differ if connection holdability is set to close cursor on commit using reflection |
620 | //and statement is getting created with holdability true |
621 | //Another instance of holdability of connection and statement not being same is when connection holdability is hold cursor |
622 | //over commit and statement is being created with holdability false |
623 | int defaultHoldability = JDBC30Translation.HOLD_CURSORS_OVER_COMMIT; |
624 | try { |
625 | Method sh = conn.getClass().getMethod("getHoldability", null); |
626 | defaultHoldability = ((Integer)sh.invoke(conn, null)).intValue(); |
627 | } catch( Exception e) { |
628 | throw PublicAPI.wrapStandardException( StandardException.plainWrapException( e)); |
629 | } |
630 | return defaultHoldability; |
631 | } |
632 | |
633 | /** |
634 | * Create the right kind of statement (scrolling or not) |
635 | * off of the specified connection. |
636 | * |
637 | * @param conn The connection. |
638 | * @param scrollType The scroll type of the cursor. |
639 | * |
640 | * @return The statement. |
641 | */ |
642 | public Statement createStatement(Connection conn, int scrollType, int holdType) |
643 | throws SQLException |
644 | { |
645 | //following if is used to make sure we are not trying to create a statement with holdability different that the connection |
646 | //holdability. This is because jdk13 and lower does not have support for that. |
647 | //The holdability of connection and statement can differ if connection holdability is set to close cursor on commit using reflection |
648 | //and statement is getting created with holdability true |
649 | //Another instance of holdability of connection and statement not being same is when connection holdability is hold cursor |
650 | //over commit and statement is being created with holdability false |
651 | if (holdType != getHoldability(conn)) |
652 | { |
653 | throw ijException.holdCursorsNotSupported(); |
654 | } |
655 | |
656 | Statement stmt; |
657 | try { |
658 | stmt = conn.createStatement(scrollType, JDBC20Translation.CONCUR_READ_ONLY); |
659 | } catch(AbstractMethodError ame) { |
660 | //because weblogic 4.5 doesn't yet implement jdbc 2.0 interfaces, need to go back |
661 | //to jdbc 1.x functionality |
662 | stmt = conn.createStatement(); |
663 | } |
664 | return stmt; |
665 | } |
666 | |
667 | /** |
668 | * Position on the specified row of the specified ResultSet. |
669 | * |
670 | * @param rs The specified ResultSet. |
671 | * @param row The row # to move to. |
672 | * (Negative means from the end of the result set.) |
673 | * |
674 | * @return NULL. |
675 | * |
676 | * @exception SQLException thrown on error. |
677 | * (absolute() not supported pre-JDBC2.0) |
678 | */ |
679 | public ijResult absolute(ResultSet rs, int row) |
680 | throws SQLException |
681 | { |
682 | boolean forwardOnly; |
683 | try { |
684 | // absolute is only allowed on scroll cursors |
685 | forwardOnly = (rs.getStatement().getResultSetType() == JDBC20Translation.TYPE_FORWARD_ONLY); |
686 | } catch (AbstractMethodError ame) { |
687 | //because weblogic 4.5 doesn't yet implement jdbc 2.0 interfaces, need to go back |
688 | //to jdbc 1.x functionality |
689 | forwardOnly = true; |
690 | } |
691 | if (forwardOnly) |
692 | { |
693 | throw ijException.forwardOnlyCursor("ABSOLUTE"); |
694 | } |
695 | |
696 | // 0 is an *VALID* value for row |
697 | return new ijRowResult(rs, rs.absolute(row)); |
698 | } |
699 | |
700 | /** |
701 | * Move the cursor position by the specified amount. |
702 | * |
703 | * @param rs The specified ResultSet. |
704 | * @param row The # of rows to move. |
705 | * (Negative means toward the beginning of the result set.) |
706 | * |
707 | * @return NULL. |
708 | * |
709 | * @exception SQLException thrown on error. |
710 | * (relative() not supported pre-JDBC2.0) |
711 | */ |
712 | public ijResult relative(ResultSet rs, int row) |
713 | throws SQLException |
714 | { |
715 | boolean forwardOnly; |
716 | try { |
717 | forwardOnly = (rs.getStatement().getResultSetType() == JDBC20Translation.TYPE_FORWARD_ONLY); |
718 | } catch (AbstractMethodError ame) { |
719 | //because weblogic 4.5 doesn't yet implement jdbc 2.0 interfaces, need to go back |
720 | //to jdbc 1.x functionality |
721 | forwardOnly = true; |
722 | } |
723 | // relative is only allowed on scroll cursors |
724 | if (forwardOnly) |
725 | { |
726 | throw ijException.forwardOnlyCursor("RELATIVE"); |
727 | } |
728 | |
729 | return new ijRowResult(rs, rs.relative(row)); |
730 | } |
731 | |
732 | /** |
733 | * Position before the first row of the specified ResultSet |
734 | * and return NULL to the user. |
735 | * |
736 | * @param rs The specified ResultSet. |
737 | * |
738 | * @return NULL. |
739 | * |
740 | * @exception SQLException thrown on error. |
741 | * (beforeFirst() not supported pre-JDBC2.0) |
742 | */ |
743 | public ijResult beforeFirst(ResultSet rs) |
744 | throws SQLException |
745 | { |
746 | boolean forwardOnly; |
747 | try { |
748 | forwardOnly = (rs.getStatement().getResultSetType() == JDBC20Translation.TYPE_FORWARD_ONLY); |
749 | } catch (AbstractMethodError ame) { |
750 | //because weblogic 4.5 doesn't yet implement jdbc 2.0 interfaces, need to go back |
751 | //to jdbc 1.x functionality |
752 | forwardOnly = true; |
753 | } |
754 | // before first is only allowed on scroll cursors |
755 | if (forwardOnly) |
756 | { |
757 | throw ijException.forwardOnlyCursor("BEFORE FIRST"); |
758 | } |
759 | |
760 | rs.beforeFirst(); |
761 | return new ijRowResult(rs, false); |
762 | } |
763 | |
764 | /** |
765 | * Position on the first row of the specified ResultSet |
766 | * and return that row to the user. |
767 | * |
768 | * @param rs The specified ResultSet. |
769 | * |
770 | * @return The first row of the ResultSet. |
771 | * |
772 | * @exception SQLException thrown on error. |
773 | * (first() not supported pre-JDBC2.0) |
774 | */ |
775 | public ijResult first(ResultSet rs) |
776 | throws SQLException |
777 | { |
778 | boolean forwardOnly; |
779 | try { |
780 | forwardOnly = (rs.getStatement().getResultSetType() == JDBC20Translation.TYPE_FORWARD_ONLY); |
781 | } catch (AbstractMethodError ame) { |
782 | //because weblogic 4.5 doesn't yet implement jdbc 2.0 interfaces, need to go back |
783 | //to jdbc 1.x functionality |
784 | forwardOnly = true; |
785 | } |
786 | // first is only allowed on scroll cursors |
787 | if (forwardOnly) |
788 | { |
789 | throw ijException.forwardOnlyCursor("FIRST"); |
790 | } |
791 | |
792 | return new ijRowResult(rs, rs.first()); |
793 | } |
794 | |
795 | /** |
796 | * Position after the last row of the specified ResultSet |
797 | * and return NULL to the user. |
798 | * |
799 | * @param rs The specified ResultSet. |
800 | * |
801 | * @return NULL. |
802 | * |
803 | * @exception SQLException thrown on error. |
804 | * (afterLast() not supported pre-JDBC2.0) |
805 | */ |
806 | public ijResult afterLast(ResultSet rs) |
807 | throws SQLException |
808 | { |
809 | boolean forwardOnly; |
810 | try { |
811 | forwardOnly = (rs.getStatement().getResultSetType() == JDBC20Translation.TYPE_FORWARD_ONLY); |
812 | } catch (AbstractMethodError ame) { |
813 | //because weblogic 4.5 doesn't yet implement jdbc 2.0 interfaces, need to go back |
814 | //to jdbc 1.x functionality |
815 | forwardOnly = true; |
816 | } |
817 | // after last is only allowed on scroll cursors |
818 | if (forwardOnly) |
819 | { |
820 | throw ijException.forwardOnlyCursor("AFTER LAST"); |
821 | } |
822 | |
823 | rs.afterLast(); |
824 | return new ijRowResult(rs, false); |
825 | } |
826 | |
827 | /** |
828 | * Position on the last row of the specified ResultSet |
829 | * and return that row to the user. |
830 | * |
831 | * @param rs The specified ResultSet. |
832 | * |
833 | * @return The last row of the ResultSet. |
834 | * |
835 | * @exception SQLException thrown on error. |
836 | * (last() not supported pre-JDBC2.0) |
837 | */ |
838 | public ijResult last(ResultSet rs) |
839 | throws SQLException |
840 | { |
841 | boolean forwardOnly; |
842 | try { |
843 | forwardOnly = (rs.getStatement().getResultSetType() == JDBC20Translation.TYPE_FORWARD_ONLY); |
844 | } catch (AbstractMethodError ame) { |
845 | //because weblogic 4.5 doesn't yet implement jdbc 2.0 interfaces, need to go back |
846 | //to jdbc 1.x functionality |
847 | forwardOnly = true; |
848 | } |
849 | // last is only allowed on scroll cursors |
850 | if (forwardOnly) |
851 | { |
852 | throw ijException.forwardOnlyCursor("LAST"); |
853 | } |
854 | |
855 | return new ijRowResult(rs, rs.last()); |
856 | } |
857 | |
858 | /** |
859 | * Position on the previous row of the specified ResultSet |
860 | * and return that row to the user. |
861 | * |
862 | * @param rs The specified ResultSet. |
863 | * |
864 | * @return The previous row of the ResultSet. |
865 | * |
866 | * @exception SQLException thrown on error. |
867 | * (previous() not supported pre-JDBC2.0) |
868 | */ |
869 | public ijResult previous(ResultSet rs) |
870 | throws SQLException |
871 | { |
872 | boolean forwardOnly; |
873 | try { |
874 | forwardOnly = (rs.getStatement().getResultSetType() == JDBC20Translation.TYPE_FORWARD_ONLY); |
875 | } catch (AbstractMethodError ame) { |
876 | //because weblogic 4.5 doesn't yet implement jdbc 2.0 interfaces, need to go back |
877 | //to jdbc 1.x functionality |
878 | forwardOnly = true; |
879 | } |
880 | // first is only allowed on scroll cursors |
881 | if (forwardOnly) |
882 | { |
883 | throw ijException.forwardOnlyCursor("PREVIOUS"); |
884 | } |
885 | |
886 | return new ijRowResult(rs, rs.previous()); |
887 | } |
888 | |
889 | /** |
890 | * Get the current row number |
891 | * |
892 | * @param rs The specified ResultSet. |
893 | * |
894 | * @return The current row number |
895 | * |
896 | * @exception SQLException thrown on error. |
897 | * (getRow() not supported pre-JDBC2.0) |
898 | */ |
899 | public int getCurrentRowNumber(ResultSet rs) |
900 | throws SQLException |
901 | { |
902 | boolean forwardOnly; |
903 | try |
904 | { |
905 | forwardOnly = (rs.getStatement().getResultSetType() == JDBC20Translation.TYPE_FORWARD_ONLY); |
906 | } catch (AbstractMethodError ame) |
907 | { |
908 | //because weblogic 4.5 doesn't yet implement jdbc 2.0 interfaces, need to go back |
909 | //to jdbc 1.x functionality |
910 | forwardOnly = true; |
911 | } |
912 | |
913 | // getCurrentRow is only allowed on scroll cursors |
914 | if (forwardOnly) |
915 | { |
916 | throw ijException.forwardOnlyCursor("GETCURRENTROWNUMBER"); |
917 | } |
918 | |
919 | return rs.getRow(); |
920 | } |
921 | |
922 | public Properties getConnAttributeDefaults () |
923 | { |
924 | return connAttributeDefaults; |
925 | } |
926 | |
927 | public final Object run() { |
928 | return getClass().getResourceAsStream(ProductGenusNames.TOOLS_INFO); |
929 | } |
930 | } |