2
Copyright (c) 2002, 2012, Oracle and/or its affiliates. All rights reserved.
5
The MySQL Connector/J is licensed under the terms of the GPLv2
6
<http://www.gnu.org/licenses/old-licenses/gpl-2.0.html>, like most MySQL Connectors.
7
There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
8
this software, see the FLOSS License Exception
9
<http://www.mysql.com/about/legal/licensing/foss-exception.html>.
11
This program is free software; you can redistribute it and/or modify it under the terms
12
of the GNU General Public License as published by the Free Software Foundation; version 2
15
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
16
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
17
See the GNU General Public License for more details.
19
You should have received a copy of the GNU General Public License along with this
20
program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
21
Floor, Boston, MA 02110-1301 USA
24
package com.mysql.jdbc;
26
import java.sql.SQLException;
27
import java.sql.Types;
30
* A ResultSetMetaData object can be used to find out about the types and
31
* properties of the columns in a ResultSet
33
* @author Mark Matthews
34
* @version $Id: ResultSetMetaData.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews
37
* @see java.sql.ResultSetMetaData
39
public class ResultSetMetaData implements java.sql.ResultSetMetaData {
40
private static int clampedGetLength(Field f) {
41
long fieldLength = f.getLength();
43
if (fieldLength > Integer.MAX_VALUE) {
44
fieldLength = Integer.MAX_VALUE;
47
return (int) fieldLength;
51
* Checks if the SQL Type is a Decimal/Number Type
58
private static final boolean isDecimalType(int type) {
77
boolean useOldAliasBehavior = false;
79
private ExceptionInterceptor exceptionInterceptor;
82
* Initialise for a result with a tuple set and a field descriptor set
85
* the array of field descriptors
87
public ResultSetMetaData(Field[] fields, boolean useOldAliasBehavior, ExceptionInterceptor exceptionInterceptor) {
89
this.useOldAliasBehavior = useOldAliasBehavior;
90
this.exceptionInterceptor = exceptionInterceptor;
94
* What's a column's table's catalog name?
97
* the first column is 1, the second is 2...
99
* @return catalog name, or "" if not applicable
101
* @throws SQLException
102
* if a database access error occurs
104
public String getCatalogName(int column) throws SQLException {
105
Field f = getField(column);
107
String database = f.getDatabaseName();
109
return (database == null) ? "" : database; //$NON-NLS-1$
113
* What's the Java character encoding name for the given column?
116
* the first column is 1, the second is 2, etc.
118
* @return the Java character encoding name for the given column, or null if
119
* no Java character encoding maps to the MySQL character set for
122
* @throws SQLException
123
* if an invalid column index is given.
125
public String getColumnCharacterEncoding(int column) throws SQLException {
126
String mysqlName = getColumnCharacterSet(column);
128
String javaName = null;
130
if (mysqlName != null) {
132
javaName = CharsetMapping.MYSQL_TO_JAVA_CHARSET_MAP.get(mysqlName);
133
} catch (RuntimeException ex) {
134
SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
144
* What's the MySQL character set name for the given column?
147
* the first column is 1, the second is 2, etc.
149
* @return the MySQL character set name for the given column
151
* @throws SQLException
152
* if an invalid column index is given.
154
public String getColumnCharacterSet(int column) throws SQLException {
155
return getField(column).getCharacterSet();
158
// --------------------------JDBC 2.0-----------------------------------
164
* Return the fully qualified name of the Java class whose instances are
165
* manufactured if ResultSet.getObject() is called to retrieve a value from
166
* the column. ResultSet.getObject() may return a subClass of the class
167
* returned by this method.
171
* the column number to retrieve information for
173
* @return the fully qualified name of the Java class whose instances are
174
* manufactured if ResultSet.getObject() is called to retrieve a
175
* value from the column.
177
* @throws SQLException
180
public String getColumnClassName(int column) throws SQLException {
181
Field f = getField(column);
183
return getClassNameForJavaType(f.getSQLType(),
186
f.isBinary() || f.isBlob(),
191
* Whats the number of columns in the ResultSet?
195
* @throws SQLException
196
* if a database access error occurs
198
public int getColumnCount() throws SQLException {
199
return this.fields.length;
203
* What is the column's normal maximum width in characters?
206
* the first column is 1, the second is 2, etc.
208
* @return the maximum width
210
* @throws SQLException
211
* if a database access error occurs
213
public int getColumnDisplaySize(int column) throws SQLException {
214
Field f = getField(column);
216
int lengthInBytes = clampedGetLength(f);
218
return lengthInBytes / f.getMaxBytesPerCharacter();
222
* What is the suggested column title for use in printouts and displays?
225
* the first column is 1, the second is 2, etc.
227
* @return the column label
229
* @throws SQLException
230
* if a database access error occurs
232
public String getColumnLabel(int column) throws SQLException {
233
if (this.useOldAliasBehavior) {
234
return getColumnName(column);
237
return getField(column).getColumnLabel();
241
* What's a column's name?
244
* the first column is 1, the second is 2, etc.
246
* @return the column name
248
* @throws SQLException
249
* if a databvase access error occurs
251
public String getColumnName(int column) throws SQLException {
252
if (this.useOldAliasBehavior) {
253
return getField(column).getName();
256
String name = getField(column).getNameNoAliases();
258
if (name != null && name.length() == 0) {
259
return getField(column).getName();
266
* What is a column's SQL Type? (java.sql.Type int)
269
* the first column is 1, the second is 2, etc.
271
* @return the java.sql.Type value
273
* @throws SQLException
274
* if a database access error occurs
276
* @see java.sql.Types
278
public int getColumnType(int column) throws SQLException {
279
return getField(column).getSQLType();
283
* Whats is the column's data source specific type name?
286
* the first column is 1, the second is 2, etc.
288
* @return the type name
290
* @throws SQLException
291
* if a database access error occurs
293
public String getColumnTypeName(int column) throws java.sql.SQLException {
294
Field field = getField(column);
296
int mysqlType = field.getMysqlType();
297
int jdbcType = field.getSQLType();
300
case MysqlDefs.FIELD_TYPE_BIT:
302
case MysqlDefs.FIELD_TYPE_DECIMAL:
303
case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
304
return field.isUnsigned() ? "DECIMAL UNSIGNED" : "DECIMAL";
306
case MysqlDefs.FIELD_TYPE_TINY:
307
return field.isUnsigned() ? "TINYINT UNSIGNED" : "TINYINT";
309
case MysqlDefs.FIELD_TYPE_SHORT:
310
return field.isUnsigned() ? "SMALLINT UNSIGNED" : "SMALLINT";
312
case MysqlDefs.FIELD_TYPE_LONG:
313
return field.isUnsigned() ? "INT UNSIGNED" : "INT";
315
case MysqlDefs.FIELD_TYPE_FLOAT:
316
return field.isUnsigned() ? "FLOAT UNSIGNED" : "FLOAT";
318
case MysqlDefs.FIELD_TYPE_DOUBLE:
319
return field.isUnsigned() ? "DOUBLE UNSIGNED" : "DOUBLE";
321
case MysqlDefs.FIELD_TYPE_NULL:
322
return "NULL"; //$NON-NLS-1$
324
case MysqlDefs.FIELD_TYPE_TIMESTAMP:
325
return "TIMESTAMP"; //$NON-NLS-1$
327
case MysqlDefs.FIELD_TYPE_LONGLONG:
328
return field.isUnsigned() ? "BIGINT UNSIGNED" : "BIGINT";
330
case MysqlDefs.FIELD_TYPE_INT24:
331
return field.isUnsigned() ? "MEDIUMINT UNSIGNED" : "MEDIUMINT";
333
case MysqlDefs.FIELD_TYPE_DATE:
334
return "DATE"; //$NON-NLS-1$
336
case MysqlDefs.FIELD_TYPE_TIME:
337
return "TIME"; //$NON-NLS-1$
339
case MysqlDefs.FIELD_TYPE_DATETIME:
340
return "DATETIME"; //$NON-NLS-1$
342
case MysqlDefs.FIELD_TYPE_TINY_BLOB:
343
return "TINYBLOB"; //$NON-NLS-1$
345
case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
346
return "MEDIUMBLOB"; //$NON-NLS-1$
348
case MysqlDefs.FIELD_TYPE_LONG_BLOB:
349
return "LONGBLOB"; //$NON-NLS-1$
351
case MysqlDefs.FIELD_TYPE_BLOB:
352
if (getField(column).isBinary()) {
353
return "BLOB";//$NON-NLS-1$
356
return "TEXT";//$NON-NLS-1$
358
case MysqlDefs.FIELD_TYPE_VARCHAR:
359
return "VARCHAR"; //$NON-NLS-1$
361
case MysqlDefs.FIELD_TYPE_VAR_STRING:
362
if (jdbcType == Types.VARBINARY) {
366
return "VARCHAR"; //$NON-NLS-1$
368
case MysqlDefs.FIELD_TYPE_STRING:
369
if (jdbcType == Types.BINARY) {
373
return "CHAR"; //$NON-NLS-1$
375
case MysqlDefs.FIELD_TYPE_ENUM:
376
return "ENUM"; //$NON-NLS-1$
378
case MysqlDefs.FIELD_TYPE_YEAR:
379
return "YEAR"; // $NON_NLS-1$
381
case MysqlDefs.FIELD_TYPE_SET:
382
return "SET"; //$NON-NLS-1$
384
case MysqlDefs.FIELD_TYPE_GEOMETRY:
385
return "GEOMETRY"; //$NON-NLS-1$
388
return "UNKNOWN"; //$NON-NLS-1$
393
* Returns the field instance for the given column index
396
* the column number to retrieve a field instance for
398
* @return the field instance for the given column index
400
* @throws SQLException
403
protected Field getField(int columnIndex) throws SQLException {
404
if ((columnIndex < 1) || (columnIndex > this.fields.length)) {
405
throw SQLError.createSQLException(Messages.getString("ResultSetMetaData.46"), //$NON-NLS-1$
406
SQLError.SQL_STATE_INVALID_COLUMN_NUMBER, this.exceptionInterceptor);
409
return this.fields[columnIndex - 1];
413
* What is a column's number of decimal digits.
416
* the first column is 1, the second is 2...
418
* @return the precision
420
* @throws SQLException
421
* if a database access error occurs
423
public int getPrecision(int column) throws SQLException {
424
Field f = getField(column);
426
// if (f.getMysqlType() == MysqlDefs.FIELD_TYPE_NEW_DECIMAL) {
427
// return f.getLength();
430
if (isDecimalType(f.getSQLType())) {
431
if (f.getDecimals() > 0) {
432
return clampedGetLength(f) - 1 + f.getPrecisionAdjustFactor();
435
return clampedGetLength(f) + f.getPrecisionAdjustFactor();
438
switch (f.getMysqlType()) {
439
case MysqlDefs.FIELD_TYPE_TINY_BLOB:
440
case MysqlDefs.FIELD_TYPE_BLOB:
441
case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
442
case MysqlDefs.FIELD_TYPE_LONG_BLOB:
443
return clampedGetLength(f); // this may change in the future
444
// for now, the server only
445
// returns FIELD_TYPE_BLOB for _all_
446
// BLOB types, but varying lengths
447
// indicating the _maximum_ size
448
// for each BLOB type.
450
return clampedGetLength(f) / f.getMaxBytesPerCharacter();
456
* What is a column's number of digits to the right of the decimal point?
459
* the first column is 1, the second is 2...
463
* @throws SQLException
464
* if a database access error occurs
466
public int getScale(int column) throws SQLException {
467
Field f = getField(column);
469
if (isDecimalType(f.getSQLType())) {
470
return f.getDecimals();
477
* What is a column's table's schema? This relies on us knowing the table
478
* name. The JDBC specification allows us to return "" if this is not
482
* the first column is 1, the second is 2...
486
* @throws SQLException
487
* if a database access error occurs
489
public String getSchemaName(int column) throws SQLException {
490
return ""; //$NON-NLS-1$
494
* Whats a column's table's name?
497
* the first column is 1, the second is 2...
499
* @return column name, or "" if not applicable
501
* @throws SQLException
502
* if a database access error occurs
504
public String getTableName(int column) throws SQLException {
505
if (this.useOldAliasBehavior) {
506
return getField(column).getTableName();
509
return getField(column).getTableNameNoAliases();
513
* Is the column automatically numbered (and thus read-only)
516
* the first column is 1, the second is 2...
520
* @throws SQLException
521
* if a database access error occurs
523
public boolean isAutoIncrement(int column) throws SQLException {
524
Field f = getField(column);
526
return f.isAutoIncrement();
530
* Does a column's case matter?
533
* the first column is 1, the second is 2...
537
* @throws java.sql.SQLException
538
* if a database access error occurs
540
public boolean isCaseSensitive(int column) throws java.sql.SQLException {
541
Field field = getField(column);
543
int sqlType = field.getSQLType();
556
case Types.TIMESTAMP:
561
case Types.LONGVARCHAR:
563
if (field.isBinary()) {
567
String collationName = field.getCollation();
569
return ((collationName != null) && !collationName.endsWith("_ci"));
577
* Is the column a cash value?
580
* the first column is 1, the second is 2...
582
* @return true if its a cash column
584
* @throws SQLException
585
* if a database access error occurs
587
public boolean isCurrency(int column) throws SQLException {
592
* Will a write on this column definately succeed?
595
* the first column is 1, the second is 2, etc..
599
* @throws SQLException
600
* if a database access error occurs
602
public boolean isDefinitelyWritable(int column) throws SQLException {
603
return isWritable(column);
607
* Can you put a NULL in this column?
610
* the first column is 1, the second is 2...
612
* @return one of the columnNullable values
614
* @throws SQLException
615
* if a database access error occurs
617
public int isNullable(int column) throws SQLException {
618
if (!getField(column).isNotNull()) {
619
return java.sql.ResultSetMetaData.columnNullable;
622
return java.sql.ResultSetMetaData.columnNoNulls;
626
* Is the column definitely not writable?
629
* the first column is 1, the second is 2, etc.
633
* @throws SQLException
634
* if a database access error occurs
636
public boolean isReadOnly(int column) throws SQLException {
637
return getField(column).isReadOnly();
641
* Can the column be used in a WHERE clause? Basically for this, I split the
642
* functions into two types: recognised types (which are always useable),
643
* and OTHER types (which may or may not be useable). The OTHER types, for
644
* now, I will assume they are useable. We should really query the catalog
645
* to see if they are useable.
648
* the first column is 1, the second is 2...
650
* @return true if they can be used in a WHERE clause
652
* @throws SQLException
653
* if a database access error occurs
655
public boolean isSearchable(int column) throws SQLException {
660
* Is the column a signed number?
663
* the first column is 1, the second is 2...
667
* @throws SQLException
668
* if a database access error occurs
670
public boolean isSigned(int column) throws SQLException {
671
Field f = getField(column);
672
int sqlType = f.getSQLType();
684
return !f.isUnsigned();
688
case Types.TIMESTAMP:
697
* Is it possible for a write on the column to succeed?
700
* the first column is 1, the second is 2, etc.
704
* @throws SQLException
705
* if a database access error occurs
707
public boolean isWritable(int column) throws SQLException {
708
return !isReadOnly(column);
712
* Returns a string representation of this object
716
public String toString() {
717
StringBuffer toStringBuf = new StringBuffer();
718
toStringBuf.append(super.toString());
719
toStringBuf.append(" - Field level information: "); //$NON-NLS-1$
721
for (int i = 0; i < this.fields.length; i++) {
722
toStringBuf.append("\n\t"); //$NON-NLS-1$
723
toStringBuf.append(this.fields[i].toString());
726
return toStringBuf.toString();
729
static String getClassNameForJavaType(int javaType,
730
boolean isUnsigned, int mysqlTypeIfKnown,
731
boolean isBinaryOrBlob,
732
boolean isOpaqueBinary) {
736
return "java.lang.Boolean"; //$NON-NLS-1$
741
return "java.lang.Integer"; //$NON-NLS-1$
744
return "java.lang.Integer"; //$NON-NLS-1$
749
return "java.lang.Integer"; //$NON-NLS-1$
752
return "java.lang.Integer"; //$NON-NLS-1$
757
mysqlTypeIfKnown == MysqlDefs.FIELD_TYPE_INT24) {
758
return "java.lang.Integer"; //$NON-NLS-1$
761
return "java.lang.Long"; //$NON-NLS-1$
766
return "java.lang.Long"; //$NON-NLS-1$
769
return "java.math.BigInteger"; //$NON-NLS-1$
773
return "java.math.BigDecimal"; //$NON-NLS-1$
776
return "java.lang.Float"; //$NON-NLS-1$
780
return "java.lang.Double"; //$NON-NLS-1$
784
case Types.LONGVARCHAR:
785
if (!isOpaqueBinary) {
786
return "java.lang.String"; //$NON-NLS-1$
792
case Types.VARBINARY:
793
case Types.LONGVARBINARY:
795
if (mysqlTypeIfKnown == MysqlDefs.FIELD_TYPE_GEOMETRY) {
797
} else if (isBinaryOrBlob) {
800
return "java.lang.String";
804
return "java.sql.Date"; //$NON-NLS-1$
807
return "java.sql.Time"; //$NON-NLS-1$
809
case Types.TIMESTAMP:
810
return "java.sql.Timestamp"; //$NON-NLS-1$
813
return "java.lang.Object"; //$NON-NLS-1$
818
* Returns true if this either implements the interface argument or is directly or indirectly a wrapper
819
* for an object that does. Returns false otherwise. If this implements the interface then return true,
820
* else if this is a wrapper then return the result of recursively calling <code>isWrapperFor</code> on the wrapped
821
* object. If this does not implement the interface and is not a wrapper, return false.
822
* This method should be implemented as a low-cost operation compared to <code>unwrap</code> so that
823
* callers can use this method to avoid expensive <code>unwrap</code> calls that may fail. If this method
824
* returns true then calling <code>unwrap</code> with the same argument should succeed.
826
* @param interfaces a Class defining an interface.
827
* @return true if this implements the interface or directly or indirectly wraps an object that does.
828
* @throws java.sql.SQLException if an error occurs while determining whether this is a wrapper
829
* for an object with the given interface.
832
public boolean isWrapperFor(Class<?> iface) throws SQLException {
833
// This works for classes that aren't actually wrapping
835
return iface.isInstance(this);
839
* Returns an object that implements the given interface to allow access to non-standard methods,
840
* or standard methods not exposed by the proxy.
841
* The result may be either the object found to implement the interface or a proxy for that object.
842
* If the receiver implements the interface then that is the object. If the receiver is a wrapper
843
* and the wrapped object implements the interface then that is the object. Otherwise the object is
844
* the result of calling <code>unwrap</code> recursively on the wrapped object. If the receiver is not a
845
* wrapper and does not implement the interface, then an <code>SQLException</code> is thrown.
847
* @param iface A Class defining an interface that the result must implement.
848
* @return an object that implements the interface. May be a proxy for the actual implementing object.
849
* @throws java.sql.SQLException If no object found that implements the interface
852
public Object unwrap(Class<?> iface) throws java.sql.SQLException {
854
// This works for classes that aren't actually wrapping
856
return Util.cast(iface, this);
857
} catch (ClassCastException cce) {
858
throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(),
859
SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor);