| 1 25 package com.mysql.jdbc; 26 27 import java.io.InputStream ; 28 import java.io.Reader ; 29 30 import java.math.BigDecimal ; 31 32 import java.net.URL ; 33 34 import java.sql.Array ; 35 import java.sql.Blob ; 36 import java.sql.Clob ; 37 import java.sql.Date ; 38 import java.sql.ParameterMetaData ; 39 import java.sql.Ref ; 40 import java.sql.SQLException ; 41 import java.sql.Time ; 42 import java.sql.Timestamp ; 43 import java.sql.Types ; 44 45 import java.util.ArrayList ; 46 import java.util.Calendar ; 47 import java.util.HashMap ; 48 import java.util.Iterator ; 49 import java.util.List ; 50 import java.util.Locale ; 51 import java.util.Map ; 52 53 60 public class CallableStatement extends PreparedStatement implements 61 java.sql.CallableStatement { 62 class CallableStatementParam { 63 int desiredJdbcType; 64 65 int index; 66 67 boolean isIn; 68 69 boolean isOut; 70 71 String paramName; 72 73 int jdbcType; 74 String typeName; 75 int precision; 76 int scale; 77 short nullability; 78 int inOutModifier; 79 80 CallableStatementParam(String name, int idx, boolean in, 81 boolean out, int jdbcType, String typeName, 82 int precision, int scale, short nullability, 83 int inOutModifier) { 84 this.paramName = name; 85 this.isIn = in; 86 this.isOut = out; 87 this.index = idx; 88 89 this.jdbcType = jdbcType; 90 this.typeName = typeName; 91 this.precision = precision; 92 this.scale = scale; 93 this.nullability = nullability; 94 this.inOutModifier = inOutModifier; 95 } 96 97 102 protected Object clone() throws CloneNotSupportedException { 103 return super.clone(); 104 } 105 } 106 107 class CallableStatementParamInfo implements ParameterMetaData { 108 String catalogInUse; 109 110 boolean isFunctionCall; 111 112 String nativeSql; 113 114 int numParameters; 115 116 List parameterList; 117 118 Map parameterMap; 119 120 CallableStatementParamInfo(java.sql.ResultSet paramTypesRs) 121 throws SQLException { 122 boolean hadRows = paramTypesRs.last(); 123 124 this.nativeSql = originalSql; 125 this.catalogInUse = currentCatalog; 126 isFunctionCall = callingStoredFunction; 127 128 if (hadRows) { 129 this.numParameters = paramTypesRs.getRow(); 130 131 this.parameterList = new ArrayList (this.numParameters); 132 this.parameterMap = new HashMap (this.numParameters); 133 134 paramTypesRs.beforeFirst(); 135 136 addParametersFromDBMD(paramTypesRs); 137 } else { 138 this.numParameters = 0; 139 } 140 } 141 142 private void addParametersFromDBMD(java.sql.ResultSet paramTypesRs) 143 throws SQLException { 144 int i = 0; 145 146 if (isFunctionCall) { 147 paramTypesRs.next(); 149 } 150 151 while (paramTypesRs.next()) { 152 String paramName = paramTypesRs.getString(4); 153 int inOutModifier = paramTypesRs.getInt(5); 154 155 boolean isOutParameter = false; 156 boolean isInParameter = false; 157 158 if (inOutModifier == DatabaseMetaData.procedureColumnInOut) { 159 isOutParameter = true; 160 isInParameter = true; 161 } else if (inOutModifier == DatabaseMetaData.procedureColumnIn) { 162 isOutParameter = false; 163 isInParameter = true; 164 } else if (inOutModifier == DatabaseMetaData.procedureColumnOut) { 165 isOutParameter = true; 166 isInParameter = false; 167 } 168 169 int jdbcType = paramTypesRs.getInt(6); 170 String typeName = paramTypesRs.getString(7); 171 int precision = paramTypesRs.getInt(8); 172 int scale = paramTypesRs.getInt(10); 173 short nullability = paramTypesRs.getShort(12); 174 175 CallableStatementParam paramInfoToAdd = new CallableStatementParam( 176 paramName, i++, isInParameter, isOutParameter, 177 jdbcType, typeName, precision, scale, nullability, 178 inOutModifier); 179 180 this.parameterList.add(paramInfoToAdd); 181 this.parameterMap.put(paramName, paramInfoToAdd); 182 } 183 } 184 185 190 protected Object clone() throws CloneNotSupportedException { 191 return super.clone(); 193 } 194 195 CallableStatementParam getParameter(int index) { 196 return (CallableStatementParam) this.parameterList.get(index); 197 } 198 199 CallableStatementParam getParameter(String name) { 200 return (CallableStatementParam) this.parameterMap.get(name); 201 } 202 203 Iterator iterator() { 204 return this.parameterList.iterator(); 205 } 206 207 int numberOfParameters() { 208 return this.numParameters; 209 } 210 211 public int getParameterCount() throws SQLException { 212 return this.parameterList.size(); 213 } 214 215 public int isNullable(int arg0) throws SQLException { 216 checkBounds(arg0); 217 218 return getParameter(arg0 - 1).nullability; 219 } 220 221 public boolean isSigned(int arg0) throws SQLException { 222 checkBounds(arg0); 223 224 return false; 225 } 226 227 public int getPrecision(int arg0) throws SQLException { 228 checkBounds(arg0); 229 230 return getParameter(arg0 - 1).precision; 231 } 232 233 public int getScale(int arg0) throws SQLException { 234 checkBounds(arg0); 235 236 return getParameter(arg0 - 1).scale; 237 } 238 239 public int getParameterType(int arg0) throws SQLException { 240 checkBounds(arg0); 241 242 return getParameter(arg0 - 1).jdbcType; 243 } 244 245 public String getParameterTypeName(int arg0) throws SQLException { 246 checkBounds(arg0); 247 248 return getParameter(arg0 - 1).typeName; 249 } 250 251 public String getParameterClassName(int arg0) throws SQLException { 252 return null; 254 } 255 256 public int getParameterMode(int arg0) throws SQLException { 257 checkBounds(arg0); 258 259 return getParameter(arg0 - 1).inOutModifier; 260 } 261 262 protected void checkBounds(int paramIndex) throws SQLException { 263 int localParamIndex = paramIndex - 1; 264 265 if ((paramIndex < 0) 266 || (localParamIndex >= this.numParameters)) { 267 throw new SQLException ( 268 Messages.getString("CallableStatement.11") + paramIndex + Messages.getString("CallableStatement.12") + numParameters + Messages.getString("CallableStatement.13"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } 272 } 273 } 274 275 private final static int NOT_OUTPUT_PARAMETER_INDICATOR = Integer.MIN_VALUE; 276 277 private final static String PARAMETER_NAMESPACE_PREFIX = "@com_mysql_jdbc_outparam_"; 279 private static String mangleParameterName(String origParameterName) { 280 if (origParameterName == null) { 281 return null; 282 } 283 284 int offset = 0; 285 286 if (origParameterName.length() > 0 287 && origParameterName.charAt(0) == '@') { 288 offset = 1; 289 } 290 291 StringBuffer paramNameBuf = new StringBuffer (PARAMETER_NAMESPACE_PREFIX 292 .length() 293 + origParameterName.length()); 294 paramNameBuf.append(PARAMETER_NAMESPACE_PREFIX); 295 paramNameBuf.append(origParameterName.substring(offset)); 296 297 return paramNameBuf.toString(); 298 } 299 300 private boolean callingStoredFunction = false; 301 302 private ResultSet functionReturnValueResults; 303 304 private boolean hasOutputParams = false; 305 306 private ResultSet outputParameterResults; 309 310 private boolean outputParamWasNull = false; 311 312 private int[] parameterIndexToRsIndex; 313 314 protected CallableStatementParamInfo paramInfo; 315 316 private CallableStatementParam returnValueParam; 317 318 329 public CallableStatement(Connection conn, 330 CallableStatementParamInfo paramInfo) throws SQLException { 331 super(conn, paramInfo.nativeSql, paramInfo.catalogInUse); 332 333 this.paramInfo = paramInfo; 334 this.callingStoredFunction = this.paramInfo.isFunctionCall; 335 } 336 337 348 public CallableStatement(Connection conn, String catalog) 349 throws SQLException { 350 super(conn, catalog, null); 351 352 determineParameterTypes(); 353 } 354 355 368 public CallableStatement(Connection conn, String sql, String catalog, 369 boolean isFunctionCall) throws SQLException { 370 super(conn, sql, catalog); 371 372 this.callingStoredFunction = isFunctionCall; 373 374 determineParameterTypes(); 375 } 376 377 382 public void addBatch() throws SQLException { 383 setOutParams(); 384 385 super.addBatch(); 386 } 387 388 private CallableStatementParam checkIsOutputParam(int paramIndex) 389 throws SQLException { 390 391 if (this.callingStoredFunction) { 392 if (paramIndex == 1) { 393 394 if (this.returnValueParam == null) { 395 this.returnValueParam = new CallableStatementParam("", 0, 396 false, true, Types.VARCHAR, "VARCHAR", 0, 0, 397 DatabaseMetaData.attributeNullableUnknown, 398 DatabaseMetaData.procedureColumnReturn); 399 } 400 401 return this.returnValueParam; 402 } 403 404 paramIndex--; 406 } 407 408 checkParameterIndexBounds(paramIndex); 409 410 int localParamIndex = paramIndex - 1; 411 412 CallableStatementParam paramDescriptor = this.paramInfo 413 .getParameter(localParamIndex); 414 415 if (!paramDescriptor.isOut) { 416 throw new SQLException ( 417 Messages.getString("CallableStatement.9") + paramIndex + Messages.getString("CallableStatement.10"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); 420 } 421 422 this.hasOutputParams = true; 423 424 return paramDescriptor; 425 } 426 427 434 private void checkParameterIndexBounds(int paramIndex) throws SQLException { 435 this.paramInfo.checkBounds(paramIndex); 436 } 437 438 446 private void checkStreamability() throws SQLException { 447 if (this.hasOutputParams && createStreamingResultSet()) { 448 throw new SQLException (Messages.getString("CallableStatement.14"), SQLError.SQL_STATE_DRIVER_NOT_CAPABLE); 450 } 451 } 452 453 private void determineParameterTypes() throws SQLException { 454 java.sql.ResultSet paramTypesRs = null; 455 456 try { 457 String procName = extractProcedureName(); 458 459 java.sql.DatabaseMetaData dbmd = this.connection.getMetaData(); 460 461 boolean useCatalog = false; 462 463 if (procName.indexOf(".") == -1) { 464 useCatalog = true; 465 } 466 467 paramTypesRs = dbmd.getProcedureColumns(this.connection 468 .versionMeetsMinimum(5, 0, 2) 469 & useCatalog ? this.currentCatalog : null, null, procName, 470 "%"); 472 this.paramInfo = new CallableStatementParamInfo(paramTypesRs); 473 } finally { 474 SQLException sqlExRethrow = null; 475 476 if (paramTypesRs != null) { 477 try { 478 paramTypesRs.close(); 479 } catch (SQLException sqlEx) { 480 sqlExRethrow = sqlEx; 481 } 482 483 paramTypesRs = null; 484 } 485 486 if (sqlExRethrow != null) { 487 throw sqlExRethrow; 488 } 489 } 490 } 491 492 497 public boolean execute() throws SQLException { 498 boolean returnVal = false; 499 500 checkClosed(); 501 502 checkStreamability(); 503 504 synchronized (this.connection.getMutex()) { 505 setInOutParamsOnServer(); 506 setOutParams(); 507 508 returnVal = super.execute(); 509 510 if (this.callingStoredFunction) { 511 this.functionReturnValueResults = this.results; 512 this.functionReturnValueResults.next(); 513 this.results = null; 514 } 515 516 retrieveOutParams(); 517 } 518 519 if (!this.callingStoredFunction) { 520 return returnVal; 521 } 522 523 return false; 525 } 526 527 532 public synchronized java.sql.ResultSet executeQuery() throws SQLException { 533 checkClosed(); 534 535 checkStreamability(); 536 537 java.sql.ResultSet execResults = null; 538 539 synchronized (this.connection.getMutex()) { 540 setInOutParamsOnServer(); 541 setOutParams(); 542 543 execResults = super.executeQuery(); 544 545 retrieveOutParams(); 546 } 547 548 return execResults; 549 } 550 551 556 public synchronized int executeUpdate() throws SQLException { 557 int returnVal = -1; 558 559 checkClosed(); 560 561 checkStreamability(); 562 563 if (this.callingStoredFunction) { 564 execute(); 565 566 return -1; 567 } 568 569 synchronized (this.connection.getMutex()) { 570 setInOutParamsOnServer(); 571 setOutParams(); 572 573 returnVal = super.executeUpdate(); 574 575 retrieveOutParams(); 576 } 577 578 return returnVal; 579 } 580 581 private String extractProcedureName() throws SQLException { 582 int endCallIndex = StringUtils.indexOfIgnoreCase(this.originalSql, 584 "CALL "); int offset = 5; 586 587 if (endCallIndex == -1) { 588 endCallIndex = StringUtils.indexOfIgnoreCase(this.originalSql, 589 "SELECT "); 590 offset = 7; 591 } 592 593 if (endCallIndex != -1) { 594 StringBuffer nameBuf = new StringBuffer (); 595 596 String trimmedStatement = this.originalSql.substring( 597 endCallIndex + offset).trim(); 598 599 int statementLength = trimmedStatement.length(); 600 601 for (int i = 0; i < statementLength; i++) { 602 char c = trimmedStatement.charAt(i); 603 604 if (Character.isWhitespace(c) || (c == '(') || (c == '?')) { 605 break; 606 } 607 nameBuf.append(c); 608 609 } 610 611 return nameBuf.toString(); 612 } 613 throw new SQLException (Messages.getString("CallableStatement.1"), SQLError.SQL_STATE_GENERAL_ERROR); 615 616 } 617 618 629 private String fixParameterName(String paramNameIn) throws SQLException { 630 if ((paramNameIn == null) || (paramNameIn.length() == 0)) { 631 throw new SQLException ( 632 ((Messages.getString("CallableStatement.0") + paramNameIn) == null) ? Messages.getString("CallableStatement.15") : Messages.getString("CallableStatement.16"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } 635 636 return mangleParameterName(paramNameIn); 637 638 645 } 646 647 650 public synchronized Array getArray(int i) throws SQLException { 651 ResultSet rs = getOutputParameters(i); 652 653 Array retValue = rs.getArray(mapOutputParameterIndexToRsIndex(i)); 654 655 this.outputParamWasNull = rs.wasNull(); 656 657 return retValue; 658 } 659 660 663 public synchronized Array getArray(String parameterName) 664 throws SQLException { 665 ResultSet rs = getOutputParameters(0); 668 Array retValue = rs.getArray(fixParameterName(parameterName)); 669 670 this.outputParamWasNull = rs.wasNull(); 671 672 return retValue; 673 } 674 675 678 public synchronized BigDecimal getBigDecimal(int parameterIndex) 679 throws SQLException { 680 ResultSet rs = getOutputParameters(parameterIndex); 681 682 BigDecimal retValue = rs 683 .getBigDecimal(mapOutputParameterIndexToRsIndex(parameterIndex)); 684 685 this.outputParamWasNull = rs.wasNull(); 686 687 return retValue; 688 } 689 690 706 public synchronized BigDecimal getBigDecimal(int parameterIndex, int scale) 707 throws SQLException { 708 ResultSet rs = getOutputParameters(parameterIndex); 709 710 BigDecimal retValue = rs.getBigDecimal( 711 mapOutputParameterIndexToRsIndex(parameterIndex), scale); 712 713 this.outputParamWasNull = rs.wasNull(); 714 715 return retValue; 716 } 717 718 721 public synchronized BigDecimal getBigDecimal(String parameterName) 722 throws SQLException { 723 ResultSet rs = getOutputParameters(0); 726 BigDecimal retValue = rs.getBigDecimal(fixParameterName(parameterName)); 727 728 this.outputParamWasNull = rs.wasNull(); 729 730 return retValue; 731 } 732 733 736 public synchronized Blob getBlob(int parameterIndex) throws SQLException { 737 ResultSet rs = getOutputParameters(parameterIndex); 738 739 Blob retValue = rs 740 .getBlob(mapOutputParameterIndexToRsIndex(parameterIndex)); 741 742 this.outputParamWasNull = rs.wasNull(); 743 744 return retValue; 745 } 746 747 750 public synchronized Blob getBlob(String parameterName) throws SQLException { 751 ResultSet rs = getOutputParameters(0); 754 Blob retValue = rs.getBlob(fixParameterName(parameterName)); 755 756 this.outputParamWasNull = rs.wasNull(); 757 758 return retValue; 759 } 760 761 764 public synchronized boolean getBoolean(int parameterIndex) 765 throws SQLException { 766 ResultSet rs = getOutputParameters(parameterIndex); 767 768 boolean retValue = rs 769 .getBoolean(mapOutputParameterIndexToRsIndex(parameterIndex)); 770 771 this.outputParamWasNull = rs.wasNull(); 772 773 return retValue; 774 } 775 776 779 public synchronized boolean getBoolean(String parameterName) 780 throws SQLException { 781 ResultSet rs = getOutputParameters(0); 784 boolean retValue = rs.getBoolean(fixParameterName(parameterName)); 785 786 this.outputParamWasNull = rs.wasNull(); 787 788 return retValue; 789 } 790 791 794 public synchronized byte getByte(int parameterIndex) throws SQLException { 795 ResultSet rs = getOutputParameters(parameterIndex); 796 797 byte retValue = rs 798 .getByte(mapOutputParameterIndexToRsIndex(parameterIndex)); 799 800 this.outputParamWasNull = rs.wasNull(); 801 802 return retValue; 803 } 804 805 808 public synchronized byte getByte(String parameterName) throws SQLException { 809 ResultSet rs = getOutputParameters(0); 812 byte retValue = rs.getByte(fixParameterName(parameterName)); 813 814 this.outputParamWasNull = rs.wasNull(); 815 816 return retValue; 817 } 818 819 822 public synchronized byte[] getBytes(int parameterIndex) throws SQLException { 823 ResultSet rs = getOutputParameters(parameterIndex); 824 825 byte[] retValue = rs 826 .getBytes(mapOutputParameterIndexToRsIndex(parameterIndex)); 827 828 this.outputParamWasNull = rs.wasNull(); 829 830 return retValue; 831 } 832 833 836 public synchronized byte[] getBytes(String parameterName) 837 throws SQLException { 838 ResultSet rs = getOutputParameters(0); 841 byte[] retValue = rs.getBytes(fixParameterName(parameterName)); 842 843 this.outputParamWasNull = rs.wasNull(); 844 845 return retValue; 846 } 847 848 851 public synchronized Clob getClob(int parameterIndex) throws SQLException { 852 ResultSet rs = getOutputParameters(parameterIndex); 853 854 Clob retValue = rs 855 .getClob(mapOutputParameterIndexToRsIndex(parameterIndex)); 856 857 this.outputParamWasNull = rs.wasNull(); 858 859 return retValue; 860 } 861 862 865 public synchronized Clob getClob(String  |