com.mysql.jdbc.ResultSetMetaData Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of mysql-connector-java
Show all versions of mysql-connector-java
MySQL JDBC Type 4 driver
/*
Copyright (c) 2002, 2019, Oracle and/or its affiliates. All rights reserved.
The MySQL Connector/J is licensed under the terms of the GPLv2
, like most MySQL Connectors.
There are special exceptions to the terms and conditions of the GPLv2 as it is applied to
this software, see the FOSS License Exception
.
This program is free software; you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation; version 2
of the License.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this
program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth
Floor, Boston, MA 02110-1301 USA
*/
package com.mysql.jdbc;
import java.sql.SQLException;
import java.sql.Types;
/**
* A ResultSetMetaData object can be used to find out about the types and properties of the columns in a ResultSet
*/
public class ResultSetMetaData implements java.sql.ResultSetMetaData {
private static int clampedGetLength(Field f) {
long fieldLength = f.getLength();
if (fieldLength > Integer.MAX_VALUE) {
fieldLength = Integer.MAX_VALUE;
}
return (int) fieldLength;
}
/**
* Checks if the SQL Type is a Decimal/Number Type
*
* @param type
* SQL Type
*/
private static final boolean isDecimalType(int type) {
switch (type) {
case Types.BIT:
case Types.TINYINT:
case Types.SMALLINT:
case Types.INTEGER:
case Types.BIGINT:
case Types.FLOAT:
case Types.REAL:
case Types.DOUBLE:
case Types.NUMERIC:
case Types.DECIMAL:
return true;
}
return false;
}
Field[] fields;
boolean useOldAliasBehavior = false;
boolean treatYearAsDate = true;
private ExceptionInterceptor exceptionInterceptor;
/**
* Initialize for a result with a tuple set and a field descriptor set
*
* @param fields
* the array of field descriptors
*/
public ResultSetMetaData(Field[] fields, boolean useOldAliasBehavior, boolean treatYearAsDate, ExceptionInterceptor exceptionInterceptor) {
this.fields = fields;
this.useOldAliasBehavior = useOldAliasBehavior;
this.treatYearAsDate = treatYearAsDate;
this.exceptionInterceptor = exceptionInterceptor;
}
/**
* What's a column's table's catalog name?
*
* @param column
* the first column is 1, the second is 2...
*
* @return catalog name, or "" if not applicable
*
* @throws SQLException
* if a database access error occurs
*/
public String getCatalogName(int column) throws SQLException {
Field f = getField(column);
String database = f.getDatabaseName();
return (database == null) ? "" : database;
}
/**
* What's the Java character encoding name for the given column?
*
* @param column
* the first column is 1, the second is 2, etc.
*
* @return the Java character encoding name for the given column, or null if
* no Java character encoding maps to the MySQL character set for
* the given column.
*
* @throws SQLException
* if an invalid column index is given.
*/
public String getColumnCharacterEncoding(int column) throws SQLException {
String mysqlName = getColumnCharacterSet(column);
String javaName = null;
if (mysqlName != null) {
try {
javaName = CharsetMapping.getJavaEncodingForMysqlCharset(mysqlName);
} catch (RuntimeException ex) {
SQLException sqlEx = SQLError.createSQLException(ex.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, null);
sqlEx.initCause(ex);
throw sqlEx;
}
}
return javaName;
}
/**
* What's the MySQL character set name for the given column?
*
* @param column
* the first column is 1, the second is 2, etc.
*
* @return the MySQL character set name for the given column
*
* @throws SQLException
* if an invalid column index is given.
*/
public String getColumnCharacterSet(int column) throws SQLException {
return getField(column).getEncoding();
}
// --------------------------JDBC 2.0-----------------------------------
/**
* JDBC 2.0
*
*
* Return the fully qualified name of the Java class whose instances are manufactured if ResultSet.getObject() is called to retrieve a value from the
* column. ResultSet.getObject() may return a subClass of the class returned by this method.
*
*
* @param column
* the column number to retrieve information for
*
* @return the fully qualified name of the Java class whose instances are
* manufactured if ResultSet.getObject() is called to retrieve a
* value from the column.
*
* @throws SQLException
* if an error occurs
*/
public String getColumnClassName(int column) throws SQLException {
Field f = getField(column);
return getClassNameForJavaType(f.getSQLType(), f.isUnsigned(), f.getMysqlType(), f.isBinary() || f.isBlob(), f.isOpaqueBinary(), this.treatYearAsDate);
}
/**
* Whats the number of columns in the ResultSet?
*
* @return the number
*
* @throws SQLException
* if a database access error occurs
*/
public int getColumnCount() throws SQLException {
return this.fields.length;
}
/**
* What is the column's normal maximum width in characters?
*
* @param column
* the first column is 1, the second is 2, etc.
*
* @return the maximum width
*
* @throws SQLException
* if a database access error occurs
*/
public int getColumnDisplaySize(int column) throws SQLException {
Field f = getField(column);
int lengthInBytes = clampedGetLength(f);
return lengthInBytes / f.getMaxBytesPerCharacter();
}
/**
* What is the suggested column title for use in printouts and displays?
*
* @param column
* the first column is 1, the second is 2, etc.
*
* @return the column label
*
* @throws SQLException
* if a database access error occurs
*/
public String getColumnLabel(int column) throws SQLException {
if (this.useOldAliasBehavior) {
return getColumnName(column);
}
return getField(column).getColumnLabel();
}
/**
* What's a column's name?
*
* @param column
* the first column is 1, the second is 2, etc.
*
* @return the column name
*
* @throws SQLException
* if a databvase access error occurs
*/
public String getColumnName(int column) throws SQLException {
if (this.useOldAliasBehavior) {
return getField(column).getName();
}
String name = getField(column).getNameNoAliases();
if (name != null && name.length() == 0) {
return getField(column).getName();
}
return name;
}
/**
* What is a column's SQL Type? (java.sql.Type int)
*
* @param column
* the first column is 1, the second is 2, etc.
*
* @return the java.sql.Type value
*
* @throws SQLException
* if a database access error occurs
*
* @see java.sql.Types
*/
public int getColumnType(int column) throws SQLException {
return getField(column).getSQLType();
}
/**
* Whats is the column's data source specific type name?
*
* @param column
* the first column is 1, the second is 2, etc.
*
* @return the type name
*
* @throws SQLException
* if a database access error occurs
*/
public String getColumnTypeName(int column) throws java.sql.SQLException {
Field field = getField(column);
int mysqlType = field.getMysqlType();
int jdbcType = field.getSQLType();
switch (mysqlType) {
case MysqlDefs.FIELD_TYPE_BIT:
return "BIT";
case MysqlDefs.FIELD_TYPE_DECIMAL:
case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
return field.isUnsigned() ? "DECIMAL UNSIGNED" : "DECIMAL";
case MysqlDefs.FIELD_TYPE_TINY:
return field.isUnsigned() ? "TINYINT UNSIGNED" : "TINYINT";
case MysqlDefs.FIELD_TYPE_SHORT:
return field.isUnsigned() ? "SMALLINT UNSIGNED" : "SMALLINT";
case MysqlDefs.FIELD_TYPE_LONG:
return field.isUnsigned() ? "INT UNSIGNED" : "INT";
case MysqlDefs.FIELD_TYPE_FLOAT:
return field.isUnsigned() ? "FLOAT UNSIGNED" : "FLOAT";
case MysqlDefs.FIELD_TYPE_DOUBLE:
return field.isUnsigned() ? "DOUBLE UNSIGNED" : "DOUBLE";
case MysqlDefs.FIELD_TYPE_NULL:
return "NULL";
case MysqlDefs.FIELD_TYPE_TIMESTAMP:
return "TIMESTAMP";
case MysqlDefs.FIELD_TYPE_LONGLONG:
return field.isUnsigned() ? "BIGINT UNSIGNED" : "BIGINT";
case MysqlDefs.FIELD_TYPE_INT24:
return field.isUnsigned() ? "MEDIUMINT UNSIGNED" : "MEDIUMINT";
case MysqlDefs.FIELD_TYPE_DATE:
return "DATE";
case MysqlDefs.FIELD_TYPE_TIME:
return "TIME";
case MysqlDefs.FIELD_TYPE_DATETIME:
return "DATETIME";
case MysqlDefs.FIELD_TYPE_TINY_BLOB:
return "TINYBLOB";
case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
return "MEDIUMBLOB";
case MysqlDefs.FIELD_TYPE_LONG_BLOB:
return "LONGBLOB";
case MysqlDefs.FIELD_TYPE_BLOB:
if (getField(column).isBinary()) {
return "BLOB";
}
return "TEXT";
case MysqlDefs.FIELD_TYPE_VARCHAR:
return "VARCHAR";
case MysqlDefs.FIELD_TYPE_VAR_STRING:
if (jdbcType == Types.VARBINARY) {
return "VARBINARY";
}
return "VARCHAR";
case MysqlDefs.FIELD_TYPE_STRING:
if (jdbcType == Types.BINARY) {
return "BINARY";
}
return "CHAR";
case MysqlDefs.FIELD_TYPE_ENUM:
return "ENUM";
case MysqlDefs.FIELD_TYPE_YEAR:
return "YEAR";
case MysqlDefs.FIELD_TYPE_SET:
return "SET";
case MysqlDefs.FIELD_TYPE_GEOMETRY:
return "GEOMETRY";
case MysqlDefs.FIELD_TYPE_JSON:
return "JSON";
default:
return "UNKNOWN";
}
}
/**
* Returns the field instance for the given column index
*
* @param columnIndex
* the column number to retrieve a field instance for
*
* @return the field instance for the given column index
*
* @throws SQLException
* if an error occurs
*/
protected Field getField(int columnIndex) throws SQLException {
if ((columnIndex < 1) || (columnIndex > this.fields.length)) {
throw SQLError.createSQLException(Messages.getString("ResultSetMetaData.46"), SQLError.SQL_STATE_INVALID_COLUMN_NUMBER, this.exceptionInterceptor);
}
return this.fields[columnIndex - 1];
}
/**
* What is a column's number of decimal digits.
*
* @param column
* the first column is 1, the second is 2...
*
* @return the precision
*
* @throws SQLException
* if a database access error occurs
*/
public int getPrecision(int column) throws SQLException {
Field f = getField(column);
// if (f.getMysqlType() == MysqlDefs.FIELD_TYPE_NEW_DECIMAL) {
// return f.getLength();
// }
if (isDecimalType(f.getSQLType())) {
if (f.getDecimals() > 0) {
return clampedGetLength(f) - 1 + f.getPrecisionAdjustFactor();
}
return clampedGetLength(f) + f.getPrecisionAdjustFactor();
}
switch (f.getMysqlType()) {
case MysqlDefs.FIELD_TYPE_TINY_BLOB:
case MysqlDefs.FIELD_TYPE_BLOB:
case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
case MysqlDefs.FIELD_TYPE_LONG_BLOB:
return clampedGetLength(f); // this may change in the future for now, the server only returns FIELD_TYPE_BLOB for _all_ BLOB types, but varying
// lengths indicating the _maximum_ size for each BLOB type.
default:
return clampedGetLength(f) / f.getMaxBytesPerCharacter();
}
}
/**
* What is a column's number of digits to the right of the decimal point?
*
* @param column
* the first column is 1, the second is 2...
*
* @return the scale
*
* @throws SQLException
* if a database access error occurs
*/
public int getScale(int column) throws SQLException {
Field f = getField(column);
if (isDecimalType(f.getSQLType())) {
return f.getDecimals();
}
return 0;
}
/**
* What is a column's table's schema? This relies on us knowing the table
* name. The JDBC specification allows us to return "" if this is not
* applicable.
*
* @param column
* the first column is 1, the second is 2...
*
* @return the Schema
*
* @throws SQLException
* if a database access error occurs
*/
public String getSchemaName(int column) throws SQLException {
return "";
}
/**
* Whats a column's table's name?
*
* @param column
* the first column is 1, the second is 2...
*
* @return column name, or "" if not applicable
*
* @throws SQLException
* if a database access error occurs
*/
public String getTableName(int column) throws SQLException {
String res = this.useOldAliasBehavior ? getField(column).getTableName() : getField(column).getTableNameNoAliases();
return res == null ? "" : res;
}
/**
* Is the column automatically numbered (and thus read-only)
*
* @param column
* the first column is 1, the second is 2...
*
* @return true if so
*
* @throws SQLException
* if a database access error occurs
*/
public boolean isAutoIncrement(int column) throws SQLException {
Field f = getField(column);
return f.isAutoIncrement();
}
/**
* Does a column's case matter?
*
* @param column
* the first column is 1, the second is 2...
*
* @return true if so
*
* @throws java.sql.SQLException
* if a database access error occurs
*/
public boolean isCaseSensitive(int column) throws java.sql.SQLException {
Field field = getField(column);
int sqlType = field.getSQLType();
switch (sqlType) {
case Types.BIT:
case Types.TINYINT:
case Types.SMALLINT:
case Types.INTEGER:
case Types.BIGINT:
case Types.FLOAT:
case Types.REAL:
case Types.DOUBLE:
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
return false;
case Types.CHAR:
case Types.VARCHAR:
case Types.LONGVARCHAR:
if (field.isBinary()) {
return true;
}
String collationName = field.getCollation();
return ((collationName != null) && !collationName.endsWith("_ci"));
default:
return true;
}
}
/**
* Is the column a cash value?
*
* @param column
* the first column is 1, the second is 2...
*
* @return true if its a cash column
*
* @throws SQLException
* if a database access error occurs
*/
public boolean isCurrency(int column) throws SQLException {
return false;
}
/**
* Will a write on this column definately succeed?
*
* @param column
* the first column is 1, the second is 2, etc..
*
* @return true if so
*
* @throws SQLException
* if a database access error occurs
*/
public boolean isDefinitelyWritable(int column) throws SQLException {
return isWritable(column);
}
/**
* Can you put a NULL in this column?
*
* @param column
* the first column is 1, the second is 2...
*
* @return one of the columnNullable values
*
* @throws SQLException
* if a database access error occurs
*/
public int isNullable(int column) throws SQLException {
if (!getField(column).isNotNull()) {
return java.sql.ResultSetMetaData.columnNullable;
}
return java.sql.ResultSetMetaData.columnNoNulls;
}
/**
* Is the column definitely not writable?
*
* @param column
* the first column is 1, the second is 2, etc.
*
* @return true if so
*
* @throws SQLException
* if a database access error occurs
*/
public boolean isReadOnly(int column) throws SQLException {
return getField(column).isReadOnly();
}
/**
* Can the column be used in a WHERE clause? Basically for this, I split the
* functions into two types: recognised types (which are always useable),
* and OTHER types (which may or may not be useable). The OTHER types, for
* now, I will assume they are useable. We should really query the catalog
* to see if they are useable.
*
* @param column
* the first column is 1, the second is 2...
*
* @return true if they can be used in a WHERE clause
*
* @throws SQLException
* if a database access error occurs
*/
public boolean isSearchable(int column) throws SQLException {
return true;
}
/**
* Is the column a signed number?
*
* @param column
* the first column is 1, the second is 2...
*
* @return true if so
*
* @throws SQLException
* if a database access error occurs
*/
public boolean isSigned(int column) throws SQLException {
Field f = getField(column);
int sqlType = f.getSQLType();
switch (sqlType) {
case Types.TINYINT:
case Types.SMALLINT:
case Types.INTEGER:
case Types.BIGINT:
case Types.FLOAT:
case Types.REAL:
case Types.DOUBLE:
case Types.NUMERIC:
case Types.DECIMAL:
return !f.isUnsigned();
case Types.DATE:
case Types.TIME:
case Types.TIMESTAMP:
return false;
default:
return false;
}
}
/**
* Is it possible for a write on the column to succeed?
*
* @param column
* the first column is 1, the second is 2, etc.
*
* @return true if so
*
* @throws SQLException
* if a database access error occurs
*/
public boolean isWritable(int column) throws SQLException {
return !isReadOnly(column);
}
/**
* Returns a string representation of this object
*
* @return ...
*/
@Override
public String toString() {
StringBuilder toStringBuf = new StringBuilder();
toStringBuf.append(super.toString());
toStringBuf.append(" - Field level information: ");
for (int i = 0; i < this.fields.length; i++) {
toStringBuf.append("\n\t");
toStringBuf.append(this.fields[i].toString());
}
return toStringBuf.toString();
}
static String getClassNameForJavaType(int javaType, boolean isUnsigned, int mysqlTypeIfKnown, boolean isBinaryOrBlob, boolean isOpaqueBinary,
boolean treatYearAsDate) {
switch (javaType) {
case Types.BIT:
case Types.BOOLEAN:
return "java.lang.Boolean";
case Types.TINYINT:
if (isUnsigned) {
return "java.lang.Integer";
}
return "java.lang.Integer";
case Types.SMALLINT:
if (isUnsigned) {
return "java.lang.Integer";
}
return "java.lang.Integer";
case Types.INTEGER:
if (!isUnsigned || mysqlTypeIfKnown == MysqlDefs.FIELD_TYPE_INT24) {
return "java.lang.Integer";
}
return "java.lang.Long";
case Types.BIGINT:
if (!isUnsigned) {
return "java.lang.Long";
}
return "java.math.BigInteger";
case Types.DECIMAL:
case Types.NUMERIC:
return "java.math.BigDecimal";
case Types.REAL:
return "java.lang.Float";
case Types.FLOAT:
case Types.DOUBLE:
return "java.lang.Double";
case Types.CHAR:
case Types.VARCHAR:
case Types.LONGVARCHAR:
if (!isOpaqueBinary) {
return "java.lang.String";
}
return "[B";
case Types.BINARY:
case Types.VARBINARY:
case Types.LONGVARBINARY:
if (mysqlTypeIfKnown == MysqlDefs.FIELD_TYPE_GEOMETRY) {
return "[B";
} else if (isBinaryOrBlob) {
return "[B";
} else {
return "java.lang.String";
}
case Types.DATE:
return (treatYearAsDate || mysqlTypeIfKnown != MysqlDefs.FIELD_TYPE_YEAR) ? "java.sql.Date" : "java.lang.Short";
case Types.TIME:
return "java.sql.Time";
case Types.TIMESTAMP:
return "java.sql.Timestamp";
default:
return "java.lang.Object";
}
}
/**
* @see java.sql.Wrapper#isWrapperFor(Class)
*/
public boolean isWrapperFor(Class iface) throws SQLException {
// This works for classes that aren't actually wrapping anything
return iface.isInstance(this);
}
/**
* @see java.sql.Wrapper#unwrap(Class)
*/
public T unwrap(Class iface) throws java.sql.SQLException {
try {
// This works for classes that aren't actually wrapping anything
return iface.cast(this);
} catch (ClassCastException cce) {
throw SQLError.createSQLException("Unable to unwrap to " + iface.toString(), SQLError.SQL_STATE_ILLEGAL_ARGUMENT, this.exceptionInterceptor);
}
}
}