001 /** 002 * Copyright 2007-2008 Arthur Blake 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 package net.sf.log4jdbc; 017 018 import java.sql.Connection; 019 import java.sql.ResultSet; 020 import java.sql.SQLException; 021 import java.sql.SQLWarning; 022 import java.sql.Statement; 023 import java.util.List; 024 import java.util.ArrayList; 025 026 /** 027 * Wraps a Statement and reports method calls, returns and exceptions. 028 * 029 * jdbc 4.0 version 030 * 031 * @see StatementSpy for the jdbc 3 version. 032 * 033 * @author Arthur Blake 034 */ 035 public class StatementSpy implements Statement, Spy 036 { 037 private final SpyLogDelegator log; 038 039 /** 040 * The Connection that created this Statement. 041 */ 042 protected ConnectionSpy connectionSpy; 043 044 /** 045 * The real statement that this StatementSpy wraps. 046 */ 047 protected Statement realStatement; 048 049 /** 050 * Create a StatementSpy (JDBC 4.0 version) that wraps another Statement, 051 * for the purpose of logging all method calls, sql, exceptions and return values. 052 * 053 * @param connectionSpy Connection that created this Statement. 054 * @param realStatement real underlying Statement that this StatementSpy wraps. 055 */ 056 public StatementSpy(ConnectionSpy connectionSpy, Statement realStatement) 057 { 058 if (realStatement == null) 059 { 060 throw new IllegalArgumentException("Must pass in a non null real Statement"); 061 } 062 if (connectionSpy == null) 063 { 064 throw new IllegalArgumentException("Must pass in a non null ConnectionSpy"); 065 } 066 this.realStatement = realStatement; 067 this.connectionSpy = connectionSpy; 068 069 log = SpyLogFactory.getSpyLogDelegator(); 070 } 071 072 public String getClassType() 073 { 074 return "Statement"; 075 } 076 077 public int getConnectionNumber() 078 { 079 return connectionSpy.getConnectionNumber(); 080 } 081 082 /** 083 * Report an exception to be logged which includes timing data on a sql failure. 084 * @param methodCall description of method call and arguments passed to it that generated the exception. 085 * @param exception exception that was generated 086 * @param sql SQL associated with the call. 087 * @param execTime amount of time that the jdbc driver was chugging on the SQL before it threw an exception. 088 */ 089 protected void reportException(String methodCall, SQLException exception, String sql, long execTime) 090 { 091 log.exceptionOccured(this, methodCall, exception, sql, execTime); 092 } 093 094 /** 095 * Report an exception to be logged. 096 * @param methodCall description of method call and arguments passed to it that generated the exception. 097 * @param exception exception that was generated 098 * @param sql SQL associated with the call. 099 */ 100 protected void reportException(String methodCall, SQLException exception, String sql) 101 { 102 log.exceptionOccured(this, methodCall, exception, sql, -1L); 103 } 104 105 /** 106 * Report an exception to be logged. 107 * 108 * @param methodCall description of method call and arguments passed to it that generated the exception. 109 * @param exception exception that was generated 110 */ 111 protected void reportException(String methodCall, SQLException exception) 112 { 113 log.exceptionOccured(this, methodCall, exception, null, -1L); 114 } 115 116 /** 117 * Report (for logging) that a method returned. All the other reportReturn methods are conveniance methods that call this method. 118 * 119 * @param methodCall description of method call and arguments passed to it that returned. 120 * @param msg description of what the return value that was returned. may be an empty String for void return types. 121 */ 122 protected void reportAllReturns(String methodCall, String msg) 123 { 124 log.methodReturned(this, methodCall, msg); 125 } 126 127 /** 128 * Conveniance method to report (for logging) that a method returned a boolean value. 129 * 130 * @param methodCall description of method call and arguments passed to it that returned. 131 * @param value boolean return value. 132 * @return the boolean return value as passed in. 133 */ 134 protected boolean reportReturn(String methodCall, boolean value) 135 { 136 reportAllReturns(methodCall, "" + value); 137 return value; 138 } 139 140 /** 141 * Conveniance method to report (for logging) that a method returned a byte value. 142 * 143 * @param methodCall description of method call and arguments passed to it that returned. 144 * @param value byte return value. 145 * @return the byte return value as passed in. 146 */ 147 protected byte reportReturn(String methodCall, byte value) 148 { 149 reportAllReturns(methodCall, "" + value); 150 return value; 151 } 152 153 /** 154 * Conveniance method to report (for logging) that a method returned a int value. 155 * 156 * @param methodCall description of method call and arguments passed to it that returned. 157 * @param value int return value. 158 * @return the int return value as passed in. 159 */ 160 protected int reportReturn(String methodCall, int value) 161 { 162 reportAllReturns(methodCall, "" + value); 163 return value; 164 } 165 166 /** 167 * Conveniance method to report (for logging) that a method returned a double value. 168 * 169 * @param methodCall description of method call and arguments passed to it that returned. 170 * @param value double return value. 171 * @return the double return value as passed in. 172 */ 173 protected double reportReturn(String methodCall, double value) 174 { 175 reportAllReturns(methodCall, "" + value); 176 return value; 177 } 178 179 /** 180 * Conveniance method to report (for logging) that a method returned a short value. 181 * 182 * @param methodCall description of method call and arguments passed to it that returned. 183 * @param value short return value. 184 * @return the short return value as passed in. 185 */ 186 protected short reportReturn(String methodCall, short value) 187 { 188 reportAllReturns(methodCall, "" + value); 189 return value; 190 } 191 192 /** 193 * Conveniance method to report (for logging) that a method returned a long value. 194 * 195 * @param methodCall description of method call and arguments passed to it that returned. 196 * @param value long return value. 197 * @return the long return value as passed in. 198 */ 199 protected long reportReturn(String methodCall, long value) 200 { 201 reportAllReturns(methodCall, "" + value); 202 return value; 203 } 204 205 /** 206 * Conveniance method to report (for logging) that a method returned a float value. 207 * 208 * @param methodCall description of method call and arguments passed to it that returned. 209 * @param value float return value. 210 * @return the float return value as passed in. 211 */ 212 protected float reportReturn(String methodCall, float value) 213 { 214 reportAllReturns(methodCall, "" + value); 215 return value; 216 } 217 218 /** 219 * Conveniance method to report (for logging) that a method returned an Object. 220 * 221 * @param methodCall description of method call and arguments passed to it that returned. 222 * @param value return Object. 223 * @return the return Object as passed in. 224 */ 225 protected Object reportReturn(String methodCall, Object value) 226 { 227 reportAllReturns(methodCall, "" + value); 228 return value; 229 } 230 231 /** 232 * Conveniance method to report (for logging) that a method returned (void return type). 233 * 234 * @param methodCall description of method call and arguments passed to it that returned. 235 */ 236 protected void reportReturn(String methodCall) 237 { 238 reportAllReturns(methodCall, ""); 239 } 240 241 /** 242 * Running one-off statement sql is generally inefficient and a bad idea for various reasons, 243 * so give a warning when this is done. 244 */ 245 private static final String StatementSqlWarning = "{WARNING: Statement used to run SQL} "; 246 247 /** 248 * Report SQL for logging with a warning that it was generated from a statement. 249 * 250 * @param sql the SQL being run 251 * @param methodCall the name of the method that was running the SQL 252 */ 253 protected void reportStatementSql(String sql, String methodCall) 254 { 255 // redirect to one more method call ONLY so that stack trace search is consistent 256 // with the reportReturn calls 257 _reportSql(StatementSqlWarning + sql, methodCall); 258 } 259 260 /** 261 * Report SQL for logging with a warning that it was generated from a statement. 262 * 263 * @param execTime execution time in msec. 264 * @param sql the SQL being run 265 * @param methodCall the name of the method that was running the SQL 266 */ 267 protected void reportStatementSqlTiming(long execTime, String sql, String methodCall) 268 { 269 // redirect to one more method call ONLY so that stack trace search is consistent 270 // with the reportReturn calls 271 _reportSqlTiming(execTime, StatementSqlWarning + sql, methodCall); 272 } 273 274 /** 275 * Report SQL for logging. 276 * 277 * @param execTime execution time in msec. 278 * @param sql the SQL being run 279 * @param methodCall the name of the method that was running the SQL 280 */ 281 protected void reportSqlTiming(long execTime, String sql, String methodCall) 282 { 283 // redirect to one more method call ONLY so that stack trace search is consistent 284 // with the reportReturn calls 285 _reportSqlTiming(execTime, sql, methodCall); 286 } 287 288 /** 289 * Report SQL for logging. 290 * 291 * @param sql the SQL being run 292 * @param methodCall the name of the method that was running the SQL 293 */ 294 protected void reportSql(String sql, String methodCall) 295 { 296 // redirect to one more method call ONLY so that stack trace search is consistent 297 // with the reportReturn calls 298 _reportSql(sql, methodCall); 299 } 300 301 private void _reportSql(String sql, String methodCall) 302 { 303 log.sqlOccured(this, methodCall, sql); 304 } 305 306 private void _reportSqlTiming(long execTime, String sql, String methodCall) 307 { 308 log.sqlTimingOccured(this, execTime, methodCall, sql); 309 } 310 311 // implementation of interface methods 312 public SQLWarning getWarnings() throws SQLException 313 { 314 String methodCall = "getWarnings()"; 315 try 316 { 317 return (SQLWarning) reportReturn(methodCall, realStatement.getWarnings()); 318 } 319 catch (SQLException s) 320 { 321 reportException(methodCall, s); 322 throw s; 323 } 324 } 325 326 public int executeUpdate(String sql, String[] columnNames) throws SQLException 327 { 328 String methodCall = "executeUpdate(" + sql + ", " + columnNames + ")"; 329 reportStatementSql(sql, methodCall); 330 long tstart = System.currentTimeMillis(); 331 try 332 { 333 int result = realStatement.executeUpdate(sql, columnNames); 334 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall); 335 return reportReturn(methodCall, result); 336 } 337 catch (SQLException s) 338 { 339 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart); 340 throw s; 341 } 342 } 343 344 public boolean execute(String sql, String[] columnNames) throws SQLException 345 { 346 String methodCall = "execute(" + sql + ", " + columnNames + ")"; 347 reportStatementSql(sql, methodCall); 348 long tstart = System.currentTimeMillis(); 349 try 350 { 351 boolean result = realStatement.execute(sql, columnNames); 352 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall); 353 return reportReturn(methodCall, result); 354 } 355 catch (SQLException s) 356 { 357 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart); 358 throw s; 359 } 360 } 361 362 public void setMaxRows(int max) throws SQLException 363 { 364 String methodCall = "setMaxRows(" + max + ")"; 365 try 366 { 367 realStatement.setMaxRows(max); 368 } 369 catch (SQLException s) 370 { 371 reportException(methodCall, s); 372 throw s; 373 } 374 reportReturn(methodCall); 375 } 376 377 public boolean getMoreResults() throws SQLException 378 { 379 String methodCall = "getMoreResults()"; 380 381 try 382 { 383 return reportReturn(methodCall, realStatement.getMoreResults()); 384 } 385 catch (SQLException s) 386 { 387 reportException(methodCall, s); 388 throw s; 389 } 390 } 391 392 public void clearWarnings() throws SQLException 393 { 394 String methodCall = "clearWarnings()"; 395 try 396 { 397 realStatement.clearWarnings(); 398 } 399 catch (SQLException s) 400 { 401 reportException(methodCall, s); 402 throw s; 403 } 404 reportReturn(methodCall); 405 } 406 407 /** 408 * Tracking of current batch (see addBatch, clearBatch and executeBatch) 409 * //todo: should access to this List be synchronized? 410 */ 411 protected List currentBatch = new ArrayList(); 412 413 public void addBatch(String sql) throws SQLException 414 { 415 String methodCall = "addBatch(" + sql + ")"; 416 417 currentBatch.add(StatementSqlWarning + sql); 418 try 419 { 420 realStatement.addBatch(sql); 421 } 422 catch (SQLException s) 423 { 424 reportException(methodCall,s); 425 throw s; 426 } 427 reportReturn(methodCall); 428 } 429 430 public int getResultSetType() throws SQLException 431 { 432 String methodCall = "getResultSetType()"; 433 try 434 { 435 return reportReturn(methodCall, realStatement.getResultSetType()); 436 } 437 catch (SQLException s) 438 { 439 reportException(methodCall, s); 440 throw s; 441 } 442 } 443 444 public void clearBatch() throws SQLException 445 { 446 String methodCall = "clearBatch()"; 447 try 448 { 449 realStatement.clearBatch(); 450 } 451 catch (SQLException s) 452 { 453 reportException(methodCall, s); 454 throw s; 455 } 456 currentBatch.clear(); 457 reportReturn(methodCall); 458 } 459 460 public void setFetchDirection(int direction) throws SQLException 461 { 462 String methodCall = "setFetchDirection(" + direction + ")"; 463 try 464 { 465 realStatement.setFetchDirection(direction); 466 } 467 catch (SQLException s) 468 { 469 reportException(methodCall, s); 470 throw s; 471 } 472 reportReturn(methodCall); 473 } 474 475 public int[] executeBatch() throws SQLException 476 { 477 String methodCall = "executeBatch()"; 478 479 int j=currentBatch.size(); 480 StringBuffer batchReport = new StringBuffer("batching " + j + " statements:"); 481 482 int fieldSize = (""+j).length(); 483 484 String sql; 485 for (int i=0; i < j;) 486 { 487 sql = (String) currentBatch.get(i); 488 batchReport.append("\n"); 489 batchReport.append(Utilities.rightJustify(fieldSize,""+(++i))); 490 batchReport.append(": "); 491 batchReport.append(sql); 492 } 493 494 sql = batchReport.toString(); 495 reportSql(sql, methodCall); 496 long tstart = System.currentTimeMillis(); 497 498 int[] updateResults; 499 try 500 { 501 updateResults = realStatement.executeBatch(); 502 reportSqlTiming(System.currentTimeMillis()-tstart, sql, methodCall); 503 } 504 catch (SQLException s) 505 { 506 reportException(methodCall, s, sql, System.currentTimeMillis()-tstart); 507 throw s; 508 } 509 return (int[])reportReturn(methodCall,updateResults); 510 } 511 512 public void setFetchSize(int rows) throws SQLException 513 { 514 String methodCall = "setFetchSize(" + rows + ")"; 515 try 516 { 517 realStatement.setFetchSize(rows); 518 } 519 catch (SQLException s) 520 { 521 reportException(methodCall, s); 522 throw s; 523 } 524 reportReturn(methodCall); 525 } 526 527 public int getQueryTimeout() throws SQLException 528 { 529 String methodCall = "getQueryTimeout()"; 530 try 531 { 532 return reportReturn(methodCall, realStatement.getQueryTimeout()); 533 } 534 catch (SQLException s) 535 { 536 reportException(methodCall, s); 537 throw s; 538 } 539 } 540 541 public Connection getConnection() throws SQLException 542 { 543 String methodCall = "getConnection()"; 544 return (Connection) reportReturn(methodCall, connectionSpy); 545 } 546 547 public ResultSet getGeneratedKeys() throws SQLException 548 { 549 String methodCall = "getGeneratedKeys()"; 550 try 551 { 552 ResultSet r = realStatement.getGeneratedKeys(); 553 if (r == null) 554 { 555 return (ResultSet) reportReturn(methodCall, r); 556 } 557 else 558 { 559 return (ResultSet) reportReturn(methodCall, new ResultSetSpy(this, r)); 560 } 561 } 562 catch (SQLException s) 563 { 564 reportException(methodCall, s); 565 throw s; 566 } 567 } 568 569 public void setEscapeProcessing(boolean enable) throws SQLException 570 { 571 String methodCall = "setEscapeProcessing(" + enable + ")"; 572 try 573 { 574 realStatement.setEscapeProcessing(enable); 575 } 576 catch (SQLException s) 577 { 578 reportException(methodCall, s); 579 throw s; 580 } 581 reportReturn(methodCall); 582 } 583 584 public int getFetchDirection() throws SQLException 585 { 586 String methodCall = "getFetchDirection()"; 587 try 588 { 589 return reportReturn(methodCall, realStatement.getFetchDirection()); 590 } 591 catch (SQLException s) 592 { 593 reportException(methodCall, s); 594 throw s; 595 } 596 } 597 598 public void setQueryTimeout(int seconds) throws SQLException 599 { 600 String methodCall = "setQueryTimeout(" + seconds + ")"; 601 try 602 { 603 realStatement.setQueryTimeout(seconds); 604 } 605 catch (SQLException s) 606 { 607 reportException(methodCall, s); 608 throw s; 609 } 610 reportReturn(methodCall); 611 } 612 613 public boolean getMoreResults(int current) throws SQLException 614 { 615 String methodCall = "getMoreResults(" + current + ")"; 616 617 try 618 { 619 return reportReturn(methodCall, realStatement.getMoreResults(current)); 620 } 621 catch (SQLException s) 622 { 623 reportException(methodCall, s); 624 throw s; 625 } 626 } 627 628 public ResultSet executeQuery(String sql) throws SQLException 629 { 630 String methodCall = "executeQuery(" + sql + ")"; 631 reportStatementSql(sql, methodCall); 632 long tstart = System.currentTimeMillis(); 633 try 634 { 635 ResultSet result = realStatement.executeQuery(sql); 636 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall); 637 ResultSetSpy r = new ResultSetSpy(this, result); 638 return (ResultSet) reportReturn(methodCall, r); 639 } 640 catch (SQLException s) 641 { 642 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart); 643 throw s; 644 } 645 } 646 647 public int getMaxFieldSize() throws SQLException 648 { 649 String methodCall = "getMaxFieldSize()"; 650 try 651 { 652 return reportReturn(methodCall, realStatement.getMaxFieldSize()); 653 } 654 catch (SQLException s) 655 { 656 reportException(methodCall, s); 657 throw s; 658 } 659 } 660 661 public int executeUpdate(String sql) throws SQLException 662 { 663 String methodCall = "executeUpdate(" + sql + ")"; 664 reportStatementSql(sql, methodCall); 665 long tstart = System.currentTimeMillis(); 666 try 667 { 668 int result = realStatement.executeUpdate(sql); 669 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall); 670 return reportReturn(methodCall, result); 671 } 672 catch (SQLException s) 673 { 674 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart); 675 throw s; 676 } 677 } 678 679 public void cancel() throws SQLException 680 { 681 String methodCall = "cancel()"; 682 try 683 { 684 realStatement.cancel(); 685 } 686 catch (SQLException s) 687 { 688 reportException(methodCall, s); 689 throw s; 690 } 691 reportReturn(methodCall); 692 } 693 694 public void setCursorName(String name) throws SQLException 695 { 696 String methodCall = "setCursorName(" + name + ")"; 697 try 698 { 699 realStatement.setCursorName(name); 700 } 701 catch (SQLException s) 702 { 703 reportException(methodCall, s); 704 throw s; 705 } 706 reportReturn(methodCall); 707 } 708 709 public int getFetchSize() throws SQLException 710 { 711 String methodCall = "getFetchSize()"; 712 try 713 { 714 return reportReturn(methodCall, realStatement.getFetchSize()); 715 } 716 catch (SQLException s) 717 { 718 reportException(methodCall, s); 719 throw s; 720 } 721 } 722 723 public int getResultSetConcurrency() throws SQLException 724 { 725 String methodCall = "getResultSetConcurrency()"; 726 try 727 { 728 return reportReturn(methodCall, realStatement.getResultSetConcurrency()); 729 } 730 catch (SQLException s) 731 { 732 reportException(methodCall, s); 733 throw s; 734 } 735 } 736 737 public int getResultSetHoldability() throws SQLException 738 { 739 String methodCall = "getResultSetHoldability()"; 740 try 741 { 742 return reportReturn(methodCall, realStatement.getResultSetHoldability()); 743 } 744 catch (SQLException s) 745 { 746 reportException(methodCall, s); 747 throw s; 748 } 749 } 750 751 public boolean isClosed() throws SQLException { 752 String methodCall = "isClosed()"; 753 try 754 { 755 return reportReturn(methodCall, realStatement.isClosed()); 756 } 757 catch (SQLException s) 758 { 759 reportException(methodCall, s); 760 throw s; 761 } 762 } 763 764 public void setPoolable(boolean poolable) throws SQLException { 765 String methodCall = "setPoolable(" + poolable + ")"; 766 try 767 { 768 realStatement.setPoolable(poolable); 769 } 770 catch (SQLException s) 771 { 772 reportException(methodCall, s); 773 throw s; 774 } 775 reportReturn(methodCall); 776 } 777 778 public boolean isPoolable() throws SQLException { 779 String methodCall = "isPoolable()"; 780 try 781 { 782 return reportReturn(methodCall, realStatement.isPoolable()); 783 } 784 catch (SQLException s) 785 { 786 reportException(methodCall, s); 787 throw s; 788 } 789 } 790 791 public void setMaxFieldSize(int max) throws SQLException 792 { 793 String methodCall = "setMaxFieldSize(" + max + ")"; 794 try 795 { 796 realStatement.setMaxFieldSize(max); 797 } 798 catch (SQLException s) 799 { 800 reportException(methodCall, s); 801 throw s; 802 } 803 reportReturn(methodCall); 804 } 805 806 public boolean execute(String sql) throws SQLException 807 { 808 String methodCall = "execute(" + sql + ")"; 809 reportStatementSql(sql, methodCall); 810 long tstart = System.currentTimeMillis(); 811 try 812 { 813 boolean result = realStatement.execute(sql); 814 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall); 815 return reportReturn(methodCall, result); 816 } 817 catch (SQLException s) 818 { 819 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart); 820 throw s; 821 } 822 } 823 824 public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException 825 { 826 String methodCall = "executeUpdate(" + sql + ", " + autoGeneratedKeys + ")"; 827 reportStatementSql(sql, methodCall); 828 long tstart = System.currentTimeMillis(); 829 try 830 { 831 int result = realStatement.executeUpdate(sql, autoGeneratedKeys); 832 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall); 833 return reportReturn(methodCall, result); 834 } 835 catch (SQLException s) 836 { 837 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart); 838 throw s; 839 } 840 } 841 842 public boolean execute(String sql, int autoGeneratedKeys) throws SQLException 843 { 844 String methodCall = "execute(" + sql + ", " + autoGeneratedKeys + ")"; 845 reportStatementSql(sql, methodCall); 846 long tstart = System.currentTimeMillis(); 847 try 848 { 849 boolean result = realStatement.execute(sql, autoGeneratedKeys); 850 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall); 851 return reportReturn(methodCall, result); 852 } 853 catch (SQLException s) 854 { 855 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart); 856 throw s; 857 } 858 } 859 860 public int executeUpdate(String sql, int[] columnIndexes) throws SQLException 861 { 862 String methodCall = "executeUpdate(" + sql + ", " + columnIndexes + ")"; 863 reportStatementSql(sql, methodCall); 864 long tstart = System.currentTimeMillis(); 865 try 866 { 867 int result = realStatement.executeUpdate(sql, columnIndexes); 868 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall); 869 return reportReturn(methodCall, result); 870 } 871 catch (SQLException s) 872 { 873 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart); 874 throw s; 875 } 876 } 877 878 public boolean execute(String sql, int[] columnIndexes) throws SQLException 879 { 880 String methodCall = "execute(" + sql + ", " + columnIndexes + ")"; 881 reportStatementSql(sql, methodCall); 882 long tstart = System.currentTimeMillis(); 883 try 884 { 885 boolean result = realStatement.execute(sql, columnIndexes); 886 reportStatementSqlTiming(System.currentTimeMillis() - tstart, sql, methodCall); 887 return reportReturn(methodCall, result); 888 } 889 catch (SQLException s) 890 { 891 reportException(methodCall, s, sql, System.currentTimeMillis() - tstart); 892 throw s; 893 } 894 } 895 896 public ResultSet getResultSet() throws SQLException 897 { 898 String methodCall = "getResultSet()"; 899 try 900 { 901 ResultSet r = realStatement.getResultSet(); 902 if (r == null) 903 { 904 return (ResultSet) reportReturn(methodCall, r); 905 } 906 else 907 { 908 return (ResultSet) reportReturn(methodCall, new ResultSetSpy(this, r)); 909 } 910 } 911 catch (SQLException s) 912 { 913 reportException(methodCall, s); 914 throw s; 915 } 916 } 917 918 public int getMaxRows() throws SQLException 919 { 920 String methodCall = "getMaxRows()"; 921 try 922 { 923 return reportReturn(methodCall, realStatement.getMaxRows()); 924 } 925 catch (SQLException s) 926 { 927 reportException(methodCall, s); 928 throw s; 929 } 930 } 931 932 public void close() throws SQLException 933 { 934 String methodCall = "close()"; 935 try 936 { 937 realStatement.close(); 938 } 939 catch (SQLException s) 940 { 941 reportException(methodCall, s); 942 throw s; 943 } 944 reportReturn(methodCall); 945 } 946 947 public int getUpdateCount() throws SQLException 948 { 949 String methodCall = "getUpdateCount()"; 950 try 951 { 952 return reportReturn(methodCall, realStatement.getUpdateCount()); 953 } 954 catch (SQLException s) 955 { 956 reportException(methodCall, s); 957 throw s; 958 } 959 } 960 961 public <T> T unwrap(Class<T> iface) throws SQLException { 962 String methodCall = "unwrap(" + (iface==null?"null":iface.getName()) + ")"; 963 try 964 { 965 //todo: double check this logic 966 return (T)reportReturn(methodCall, (iface != null && (iface == Connection.class || iface == Spy.class))?(T)this:realStatement.unwrap(iface)); 967 } 968 catch (SQLException s) 969 { 970 reportException(methodCall,s); 971 throw s; 972 } 973 } 974 975 public boolean isWrapperFor(Class<?> iface) throws SQLException 976 { 977 String methodCall = "isWrapperFor(" + (iface==null?"null":iface.getName()) + ")"; 978 try 979 { 980 return reportReturn(methodCall, (iface != null && (iface == Statement.class || iface == Spy.class)) || 981 realStatement.isWrapperFor(iface)); 982 } 983 catch (SQLException s) 984 { 985 reportException(methodCall,s); 986 throw s; 987 } 988 } 989 990 }