1 /* 2 Copyright 2002-2007 MySQL AB, 2008 Sun Microsystems 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of version 2 of the GNU General Public License as 6 published by the Free Software Foundation. 7 8 There are special exceptions to the terms and conditions of the GPL 9 as it is applied to this software. View the full text of the 10 exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 11 software distribution. 12 13 This program is distributed in the hope that it will be useful, 14 but WITHOUT ANY WARRANTY; without even the implied warranty of 15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 GNU General Public License for more details. 17 18 You should have received a copy of the GNU General Public License 19 along with this program; if not, write to the Free Software 20 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 21 22 */ 23 package com.mysql.jdbc; 24 25 import java.sql.SQLException; 26 import java.sql.Types; 27 28 /** 29 * A ResultSetMetaData object can be used to find out about the types and 30 * properties of the columns in a ResultSet 31 * 32 * @author Mark Matthews 33 * @version $Id: ResultSetMetaData.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews 34 * Exp $ 35 * 36 * @see java.sql.ResultSetMetaData 37 */ 38 public class ResultSetMetaData implements java.sql.ResultSetMetaData { 39 private static int clampedGetLength(Field f) { 40 long fieldLength = f.getLength(); 41 42 if (fieldLength > Integer.MAX_VALUE) { 43 fieldLength = Integer.MAX_VALUE; 44 } 45 46 return (int) fieldLength; 47 } 48 49 /** 50 * Checks if the SQL Type is a Decimal/Number Type 51 * 52 * @param type 53 * SQL Type 54 * 55 * @return ... 56 */ 57 private static final boolean isDecimalType(int type) { 58 switch (type) { 59 case Types.BIT: 60 case Types.TINYINT: 61 case Types.SMALLINT: 62 case Types.INTEGER: 63 case Types.BIGINT: 64 case Types.FLOAT: 65 case Types.REAL: 66 case Types.DOUBLE: 67 case Types.NUMERIC: 68 case Types.DECIMAL: 69 return true; 70 } 71 72 return false; 73 } 74 75 Field[] fields; 76 boolean useOldAliasBehavior = false; 77 78 private ExceptionInterceptor exceptionInterceptor; 79 80 /** 81 * Initialise for a result with a tuple set and a field descriptor set 82 * 83 * @param fields 84 * the array of field descriptors 85 */ 86 public ResultSetMetaData(Field[] fields, boolean useOldAliasBehavior, ExceptionInterceptor exceptionInterceptor) { 87 this.fields = fields; 88 this.useOldAliasBehavior = useOldAliasBehavior; 89 this.exceptionInterceptor = exceptionInterceptor; 90 } 91 92 /** 93 * What's a column's table's catalog name? 94 * 95 * @param column 96 * the first column is 1, the second is 2... 97 * 98 * @return catalog name, or "" if not applicable 99 * 100 * @throws SQLException 101 * if a database access error occurs 102 */ 103 public String getCatalogName(int column) throws SQLException { 104 Field f = getField(column); 105 106 String database = f.getDatabaseName(); 107 108 return (database == null) ? "" : database; //$NON-NLS-1$ 109 } 110 111 /** 112 * What's the Java character encoding name for the given column? 113 * 114 * @param column 115 * the first column is 1, the second is 2, etc. 116 * 117 * @return the Java character encoding name for the given column, or null if 118 * no Java character encoding maps to the MySQL character set for 119 * the given column. 120 * 121 * @throws SQLException 122 * if an invalid column index is given. 123 */ 124 public String getColumnCharacterEncoding(int column) throws SQLException { 125 String mysqlName = getColumnCharacterSet(column); 126 127 String javaName = null; 128 129 if (mysqlName != null) { 130 javaName = CharsetMapping.getJavaEncodingForMysqlEncoding( 131 mysqlName, null); 132 } 133 134 return javaName; 135 } 136 137 /** 138 * What's the MySQL character set name for the given column? 139 * 140 * @param column 141 * the first column is 1, the second is 2, etc. 142 * 143 * @return the MySQL character set name for the given column 144 * 145 * @throws SQLException 146 * if an invalid column index is given. 147 */ 148 public String getColumnCharacterSet(int column) throws SQLException { 149 return getField(column).getCharacterSet(); 150 } 151 152 // --------------------------JDBC 2.0----------------------------------- 153 154 /** 155 * JDBC 2.0 156 * 157 * <p> 158 * Return the fully qualified name of the Java class whose instances are 159 * manufactured if ResultSet.getObject() is called to retrieve a value from 160 * the column. ResultSet.getObject() may return a subClass of the class 161 * returned by this method. 162 * </p> 163 * 164 * @param column 165 * the column number to retrieve information for 166 * 167 * @return the fully qualified name of the Java class whose instances are 168 * manufactured if ResultSet.getObject() is called to retrieve a 169 * value from the column. 170 * 171 * @throws SQLException 172 * if an error occurs 173 */ 174 public String getColumnClassName(int column) throws SQLException { 175 Field f = getField(column); 176 177 return getClassNameForJavaType(f.getSQLType(), 178 f.isUnsigned(), 179 f.getMysqlType(), 180 f.isBinary() || f.isBlob(), 181 f.isOpaqueBinary()); 182 } 183 184 /** 185 * Whats the number of columns in the ResultSet? 186 * 187 * @return the number 188 * 189 * @throws SQLException 190 * if a database access error occurs 191 */ 192 public int getColumnCount() throws SQLException { 193 return this.fields.length; 194 } 195 196 /** 197 * What is the column's normal maximum width in characters? 198 * 199 * @param column 200 * the first column is 1, the second is 2, etc. 201 * 202 * @return the maximum width 203 * 204 * @throws SQLException 205 * if a database access error occurs 206 */ 207 public int getColumnDisplaySize(int column) throws SQLException { 208 Field f = getField(column); 209 210 int lengthInBytes = clampedGetLength(f); 211 212 return lengthInBytes / f.getMaxBytesPerCharacter(); 213 } 214 215 /** 216 * What is the suggested column title for use in printouts and displays? 217 * 218 * @param column 219 * the first column is 1, the second is 2, etc. 220 * 221 * @return the column label 222 * 223 * @throws SQLException 224 * if a database access error occurs 225 */ 226 public String getColumnLabel(int column) throws SQLException { 227 if (this.useOldAliasBehavior) { 228 return getColumnName(column); 229 } 230 231 return getField(column).getColumnLabel(); 232 } 233 234 /** 235 * What's a column's name? 236 * 237 * @param column 238 * the first column is 1, the second is 2, etc. 239 * 240 * @return the column name 241 * 242 * @throws SQLException 243 * if a databvase access error occurs 244 */ 245 public String getColumnName(int column) throws SQLException { 246 if (this.useOldAliasBehavior) { 247 return getField(column).getName(); 248 } 249 250 String name = getField(column).getNameNoAliases(); 251 252 if (name != null && name.length() == 0) { 253 return getField(column).getName(); 254 } 255 256 return name; 257 } 258 259 /** 260 * What is a column's SQL Type? (java.sql.Type int) 261 * 262 * @param column 263 * the first column is 1, the second is 2, etc. 264 * 265 * @return the java.sql.Type value 266 * 267 * @throws SQLException 268 * if a database access error occurs 269 * 270 * @see java.sql.Types 271 */ 272 public int getColumnType(int column) throws SQLException { 273 return getField(column).getSQLType(); 274 } 275 276 /** 277 * Whats is the column's data source specific type name? 278 * 279 * @param column 280 * the first column is 1, the second is 2, etc. 281 * 282 * @return the type name 283 * 284 * @throws SQLException 285 * if a database access error occurs 286 */ 287 public String getColumnTypeName(int column) throws java.sql.SQLException { 288 Field field = getField(column); 289 290 int mysqlType = field.getMysqlType(); 291 int jdbcType = field.getSQLType(); 292 293 switch (mysqlType) { 294 case MysqlDefs.FIELD_TYPE_BIT: 295 return "BIT"; 296 case MysqlDefs.FIELD_TYPE_DECIMAL: 297 case MysqlDefs.FIELD_TYPE_NEW_DECIMAL: 298 return field.isUnsigned() ? "DECIMAL UNSIGNED" : "DECIMAL"; 299 300 case MysqlDefs.FIELD_TYPE_TINY: 301 return field.isUnsigned() ? "TINYINT UNSIGNED" : "TINYINT"; 302 303 case MysqlDefs.FIELD_TYPE_SHORT: 304 return field.isUnsigned() ? "SMALLINT UNSIGNED" : "SMALLINT"; 305 306 case MysqlDefs.FIELD_TYPE_LONG: 307 return field.isUnsigned() ? "INT UNSIGNED" : "INT"; 308 309 case MysqlDefs.FIELD_TYPE_FLOAT: 310 return field.isUnsigned() ? "FLOAT UNSIGNED" : "FLOAT"; 311 312 case MysqlDefs.FIELD_TYPE_DOUBLE: 313 return field.isUnsigned() ? "DOUBLE UNSIGNED" : "DOUBLE"; 314 315 case MysqlDefs.FIELD_TYPE_NULL: 316 return "NULL"; //$NON-NLS-1$ 317 318 case MysqlDefs.FIELD_TYPE_TIMESTAMP: 319 return "TIMESTAMP"; //$NON-NLS-1$ 320 321 case MysqlDefs.FIELD_TYPE_LONGLONG: 322 return field.isUnsigned() ? "BIGINT UNSIGNED" : "BIGINT"; 323 324 case MysqlDefs.FIELD_TYPE_INT24: 325 return field.isUnsigned() ? "MEDIUMINT UNSIGNED" : "MEDIUMINT"; 326 327 case MysqlDefs.FIELD_TYPE_DATE: 328 return "DATE"; //$NON-NLS-1$ 329 330 case MysqlDefs.FIELD_TYPE_TIME: 331 return "TIME"; //$NON-NLS-1$ 332 333 case MysqlDefs.FIELD_TYPE_DATETIME: 334 return "DATETIME"; //$NON-NLS-1$ 335 336 case MysqlDefs.FIELD_TYPE_TINY_BLOB: 337 return "TINYBLOB"; //$NON-NLS-1$ 338 339 case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: 340 return "MEDIUMBLOB"; //$NON-NLS-1$ 341 342 case MysqlDefs.FIELD_TYPE_LONG_BLOB: 343 return "LONGBLOB"; //$NON-NLS-1$ 344 345 case MysqlDefs.FIELD_TYPE_BLOB: 346 if (getField(column).isBinary()) { 347 return "BLOB";//$NON-NLS-1$ 348 } 349 350 return "TEXT";//$NON-NLS-1$ 351 352 case MysqlDefs.FIELD_TYPE_VARCHAR: 353 return "VARCHAR"; //$NON-NLS-1$ 354 355 case MysqlDefs.FIELD_TYPE_VAR_STRING: 356 if (jdbcType == Types.VARBINARY) { 357 return "VARBINARY"; 358 } 359 360 return "VARCHAR"; //$NON-NLS-1$ 361 362 case MysqlDefs.FIELD_TYPE_STRING: 363 if (jdbcType == Types.BINARY) { 364 return "BINARY"; 365 } 366 367 return "CHAR"; //$NON-NLS-1$ 368 369 case MysqlDefs.FIELD_TYPE_ENUM: 370 return "ENUM"; //$NON-NLS-1$ 371 372 case MysqlDefs.FIELD_TYPE_YEAR: 373 return "YEAR"; // $NON_NLS-1$ 374 375 case MysqlDefs.FIELD_TYPE_SET: 376 return "SET"; //$NON-NLS-1$ 377 378 case MysqlDefs.FIELD_TYPE_GEOMETRY: 379 return "GEOMETRY"; //$NON-NLS-1$ 380 381 default: 382 return "UNKNOWN"; //$NON-NLS-1$ 383 } 384 } 385 386 /** 387 * Returns the field instance for the given column index 388 * 389 * @param columnIndex 390 * the column number to retrieve a field instance for 391 * 392 * @return the field instance for the given column index 393 * 394 * @throws SQLException 395 * if an error occurs 396 */ 397 protected Field getField(int columnIndex) throws SQLException { 398 if ((columnIndex < 1) || (columnIndex > this.fields.length)) { 399 throw SQLError.createSQLException(Messages.getString("ResultSetMetaData.46"), //$NON-NLS-1$ 400 SQLError.SQL_STATE_INVALID_COLUMN_NUMBER, this.exceptionInterceptor); 401 } 402 403 return this.fields[columnIndex - 1]; 404 } 405 406 /** 407 * What is a column's number of decimal digits. 408 * 409 * @param column 410 * the first column is 1, the second is 2... 411 * 412 * @return the precision 413 * 414 * @throws SQLException 415 * if a database access error occurs 416 */ 417 public int getPrecision(int column) throws SQLException { 418 Field f = getField(column); 419 420 // if (f.getMysqlType() == MysqlDefs.FIELD_TYPE_NEW_DECIMAL) { 421 // return f.getLength(); 422 // } 423 424 if (isDecimalType(f.getSQLType())) { 425 if (f.getDecimals() > 0) { 426 return clampedGetLength(f) - 1 + f.getPrecisionAdjustFactor(); 427 } 428 429 return clampedGetLength(f) + f.getPrecisionAdjustFactor(); 430 } 431 432 switch (f.getMysqlType()) { 433 case MysqlDefs.FIELD_TYPE_TINY_BLOB: 434 case MysqlDefs.FIELD_TYPE_BLOB: 435 case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: 436 case MysqlDefs.FIELD_TYPE_LONG_BLOB: 437 return clampedGetLength(f); // this may change in the future 438 // for now, the server only 439 // returns FIELD_TYPE_BLOB for _all_ 440 // BLOB types, but varying lengths 441 // indicating the _maximum_ size 442 // for each BLOB type. 443 default: 444 return clampedGetLength(f) / f.getMaxBytesPerCharacter(); 445 446 } 447 } 448 449 /** 450 * What is a column's number of digits to the right of the decimal point? 451 * 452 * @param column 453 * the first column is 1, the second is 2... 454 * 455 * @return the scale 456 * 457 * @throws SQLException 458 * if a database access error occurs 459 */ 460 public int getScale(int column) throws SQLException { 461 Field f = getField(column); 462 463 if (isDecimalType(f.getSQLType())) { 464 return f.getDecimals(); 465 } 466 467 return 0; 468 } 469 470 /** 471 * What is a column's table's schema? This relies on us knowing the table 472 * name. The JDBC specification allows us to return "" if this is not 473 * applicable. 474 * 475 * @param column 476 * the first column is 1, the second is 2... 477 * 478 * @return the Schema 479 * 480 * @throws SQLException 481 * if a database access error occurs 482 */ 483 public String getSchemaName(int column) throws SQLException { 484 return ""; //$NON-NLS-1$ 485 } 486 487 /** 488 * Whats a column's table's name? 489 * 490 * @param column 491 * the first column is 1, the second is 2... 492 * 493 * @return column name, or "" if not applicable 494 * 495 * @throws SQLException 496 * if a database access error occurs 497 */ 498 public String getTableName(int column) throws SQLException { 499 if (this.useOldAliasBehavior) { 500 return getField(column).getTableName(); 501 } 502 503 return getField(column).getTableNameNoAliases(); 504 } 505 506 /** 507 * Is the column automatically numbered (and thus read-only) 508 * 509 * @param column 510 * the first column is 1, the second is 2... 511 * 512 * @return true if so 513 * 514 * @throws SQLException 515 * if a database access error occurs 516 */ 517 public boolean isAutoIncrement(int column) throws SQLException { 518 Field f = getField(column); 519 520 return f.isAutoIncrement(); 521 } 522 523 /** 524 * Does a column's case matter? 525 * 526 * @param column 527 * the first column is 1, the second is 2... 528 * 529 * @return true if so 530 * 531 * @throws java.sql.SQLException 532 * if a database access error occurs 533 */ 534 public boolean isCaseSensitive(int column) throws java.sql.SQLException { 535 Field field = getField(column); 536 537 int sqlType = field.getSQLType(); 538 539 switch (sqlType) { 540 case Types.BIT: 541 case Types.TINYINT: 542 case Types.SMALLINT: 543 case Types.INTEGER: 544 case Types.BIGINT: 545 case Types.FLOAT: 546 case Types.REAL: 547 case Types.DOUBLE: 548 case Types.DATE: 549 case Types.TIME: 550 case Types.TIMESTAMP: 551 return false; 552 553 case Types.CHAR: 554 case Types.VARCHAR: 555 case Types.LONGVARCHAR: 556 557 if (field.isBinary()) { 558 return true; 559 } 560 561 String collationName = field.getCollation(); 562 563 return ((collationName != null) && !collationName.endsWith("_ci")); 564 565 default: 566 return true; 567 } 568 } 569 570 /** 571 * Is the column a cash value? 572 * 573 * @param column 574 * the first column is 1, the second is 2... 575 * 576 * @return true if its a cash column 577 * 578 * @throws SQLException 579 * if a database access error occurs 580 */ 581 public boolean isCurrency(int column) throws SQLException { 582 return false; 583 } 584 585 /** 586 * Will a write on this column definately succeed? 587 * 588 * @param column 589 * the first column is 1, the second is 2, etc.. 590 * 591 * @return true if so 592 * 593 * @throws SQLException 594 * if a database access error occurs 595 */ 596 public boolean isDefinitelyWritable(int column) throws SQLException { 597 return isWritable(column); 598 } 599 600 /** 601 * Can you put a NULL in this column? 602 * 603 * @param column 604 * the first column is 1, the second is 2... 605 * 606 * @return one of the columnNullable values 607 * 608 * @throws SQLException 609 * if a database access error occurs 610 */ 611 public int isNullable(int column) throws SQLException { 612 if (!getField(column).isNotNull()) { 613 return java.sql.ResultSetMetaData.columnNullable; 614 } 615 616 return java.sql.ResultSetMetaData.columnNoNulls; 617 } 618 619 /** 620 * Is the column definitely not writable? 621 * 622 * @param column 623 * the first column is 1, the second is 2, etc. 624 * 625 * @return true if so 626 * 627 * @throws SQLException 628 * if a database access error occurs 629 */ 630 public boolean isReadOnly(int column) throws SQLException { 631 return getField(column).isReadOnly(); 632 } 633 634 /** 635 * Can the column be used in a WHERE clause? Basically for this, I split the 636 * functions into two types: recognised types (which are always useable), 637 * and OTHER types (which may or may not be useable). The OTHER types, for 638 * now, I will assume they are useable. We should really query the catalog 639 * to see if they are useable. 640 * 641 * @param column 642 * the first column is 1, the second is 2... 643 * 644 * @return true if they can be used in a WHERE clause 645 * 646 * @throws SQLException 647 * if a database access error occurs 648 */ 649 public boolean isSearchable(int column) throws SQLException { 650 return true; 651 } 652 653 /** 654 * Is the column a signed number? 655 * 656 * @param column 657 * the first column is 1, the second is 2... 658 * 659 * @return true if so 660 * 661 * @throws SQLException 662 * if a database access error occurs 663 */ 664 public boolean isSigned(int column) throws SQLException { 665 Field f = getField(column); 666 int sqlType = f.getSQLType(); 667 668 switch (sqlType) { 669 case Types.TINYINT: 670 case Types.SMALLINT: 671 case Types.INTEGER: 672 case Types.BIGINT: 673 case Types.FLOAT: 674 case Types.REAL: 675 case Types.DOUBLE: 676 case Types.NUMERIC: 677 case Types.DECIMAL: 678 return !f.isUnsigned(); 679 680 case Types.DATE: 681 case Types.TIME: 682 case Types.TIMESTAMP: 683 return false; 684 685 default: 686 return false; 687 } 688 } 689 690 /** 691 * Is it possible for a write on the column to succeed? 692 * 693 * @param column 694 * the first column is 1, the second is 2, etc. 695 * 696 * @return true if so 697 * 698 * @throws SQLException 699 * if a database access error occurs 700 */ 701 public boolean isWritable(int column) throws SQLException { 702 return !isReadOnly(column); 703 } 704 705 /** 706 * Returns a string representation of this object 707 * 708 * @return ... 709 */ 710 public String toString() { 711 StringBuffer toStringBuf = new StringBuffer(); 712 toStringBuf.append(super.toString()); 713 toStringBuf.append(" - Field level information: "); //$NON-NLS-1$ 714 715 for (int i = 0; i < this.fields.length; i++) { 716 toStringBuf.append("\n\t"); //$NON-NLS-1$ 717 toStringBuf.append(this.fields[i].toString()); 718 } 719 720 return toStringBuf.toString(); 721 } 722 723 static String getClassNameForJavaType(int javaType, 724 boolean isUnsigned, int mysqlTypeIfKnown, 725 boolean isBinaryOrBlob, 726 boolean isOpaqueBinary) { 727 switch (javaType) { 728 case Types.BIT: 729 case Types.BOOLEAN: 730 return "java.lang.Boolean"; //$NON-NLS-1$ 731 732 case Types.TINYINT: 733 734 if (isUnsigned) { 735 return "java.lang.Integer"; //$NON-NLS-1$ 736 } 737 738 return "java.lang.Integer"; //$NON-NLS-1$ 739 740 case Types.SMALLINT: 741 742 if (isUnsigned) { 743 return "java.lang.Integer"; //$NON-NLS-1$ 744 } 745 746 return "java.lang.Integer"; //$NON-NLS-1$ 747 748 case Types.INTEGER: 749 750 if (!isUnsigned || 751 mysqlTypeIfKnown == MysqlDefs.FIELD_TYPE_INT24) { 752 return "java.lang.Integer"; //$NON-NLS-1$ 753 } 754 755 return "java.lang.Long"; //$NON-NLS-1$ 756 757 case Types.BIGINT: 758 759 if (!isUnsigned) { 760 return "java.lang.Long"; //$NON-NLS-1$ 761 } 762 763 return "java.math.BigInteger"; //$NON-NLS-1$ 764 765 case Types.DECIMAL: 766 case Types.NUMERIC: 767 return "java.math.BigDecimal"; //$NON-NLS-1$ 768 769 case Types.REAL: 770 return "java.lang.Float"; //$NON-NLS-1$ 771 772 case Types.FLOAT: 773 case Types.DOUBLE: 774 return "java.lang.Double"; //$NON-NLS-1$ 775 776 case Types.CHAR: 777 case Types.VARCHAR: 778 case Types.LONGVARCHAR: 779 if (!isOpaqueBinary) { 780 return "java.lang.String"; //$NON-NLS-1$ 781 } 782 783 return "[B"; 784 785 case Types.BINARY: 786 case Types.VARBINARY: 787 case Types.LONGVARBINARY: 788 789 if (mysqlTypeIfKnown == MysqlDefs.FIELD_TYPE_GEOMETRY) { 790 return "[B"; 791 } else if (isBinaryOrBlob) { 792 return "[B"; 793 } else { 794 return "java.lang.String"; 795 } 796 797 case Types.DATE: 798 return "java.sql.Date"; //$NON-NLS-1$ 799 800 case Types.TIME: 801 return "java.sql.Time"; //$NON-NLS-1$ 802 803 case Types.TIMESTAMP: 804 return "java.sql.Timestamp"; //$NON-NLS-1$ 805 806 default: 807 return "java.lang.Object"; //$NON-NLS-1$ 808 } 809 } 810 811 /** 812 * Returns true if this either implements the interface argument or is directly or indirectly a wrapper 813 * for an object that does. Returns false otherwise. If this implements the interface then return true, 814 * else if this is a wrapper then return the result of recursively calling <code>isWrapperFor</code> on the wrapped 815 * object. If this does not implement the interface and is not a wrapper, return false. 816 * This method should be implemented as a low-cost operation compared to <code>unwrap</code> so that 817 * callers can use this method to avoid expensive <code>unwrap</code> calls that may fail. If this method 818 * returns true then calling <code>unwrap</code> with the same argument should succeed. 819 * 820 * @param interfaces a Class defining an interface. 821 * @return true if this implements the interface or directly or indirectly wraps an object that does. 822 * @throws java.sql.SQLException if an error occurs while determining whether this is a wrapper 823 * for an object with the given interface. 824 * @since 1.6 825 */ 826 public boolean isWrapperFor(Class iface) throws SQLException { 827 // This works for classes that aren't actually wrapping 828 // anything 829 return iface.isInstance(this); 830 } 831 832 /** 833 * Returns an object that implements the given interface to allow access to non-standard methods, 834 * or standard methods not exposed by the proxy. 835 * The result may be either the object found to implement the interface or a proxy for that object. 836 * If the receiver implements the interface then that is the object. If the receiver is a wrapper 837 * and the wrapped object implements the interface then that is the object. Otherwise the object is 838 * the result of calling <code>unwrap</code> recursively on the wrapped object. If the receiver is not a 839 * wrapper and does not implement the interface, then an <code>SQLException</code> is thrown. 840 * 841 * @param iface A Class defining an interface that the result must implement. 842 * @return an object that implements the interface. May be a proxy for the actual implementing object. 843 * @throws java.sql.SQLException If no object found that implements the interface 844 * @since 1.6 845 */ 846 public Object unwrap(Class iface) throws java.sql.SQLException { 847 try { 848 // This works for classes that aren't actually wrapping 849 // anything 850 return Util.cast(iface, this); 851 } catch (ClassCastException cce) { 852 throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), 853 SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor); 854 } 855 } 856 }