All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.mysql.jdbc.ResultSetMetaData Maven / Gradle / Ivy

/*
  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); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy