/* XXL: The eXtensible and fleXible Library for data processing
Copyright (C) 2000-2011 Prof. Dr. Bernhard Seeger
Head of the Database Research Group
Department of Mathematics and Computer Science
University of Marburg
Germany
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3 of the License, or (at your option) any later version.
This library 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
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; If not, see <http://www.gnu.org/licenses/>.
http://code.google.com/p/xxl/
*/
package xxl.core.relational.metaData;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.Comparator;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import xxl.core.functions.AbstractFunction;
import xxl.core.functions.Function;
import xxl.core.io.converters.Converter;
import xxl.core.util.Arrays;
import xxl.core.util.metaData.CompositeMetaData;
import xxl.core.util.metaData.MetaDataException;
import xxl.core.util.metaData.MetaDataProvider;
import xxl.core.util.metaData.MetaDataProviders;
/**
* This class provides static methods for dealing with instances implementing
* the interface {@link java.sql.ResultSetMetaData result set metadata}. Beside
* this methods, it contains constants for identifying columns storing data
* that is rather important for query processing and optimization.
*
* @see java.sql.ResultSetMetaData
*/
public class ResultSetMetaDatas {
/**
* This constant provides a comparator for relational metadata (result set
* metadata). The given relational metadata is compared for the number of
* columns, the column names, the column types and a possible loss of
* precision.
*/
public static final Comparator<ResultSetMetaData> RESULTSET_METADATA_COMPARATOR = new Comparator<ResultSetMetaData>() {
public int compare(ResultSetMetaData rsmd1, ResultSetMetaData rsmd2) {
try {
int compare = ((Integer)rsmd1.getColumnCount()).compareTo(rsmd2.getColumnCount());
if (compare != 0)
return compare;
for (int column = 1; column <= rsmd1.getColumnCount(); column++) {
compare = rsmd1.getColumnName(column).compareToIgnoreCase(rsmd2.getColumnName(column));
if (compare != 0)
return compare;
compare = ((Integer)rsmd1.getColumnType(column)).compareTo(rsmd2.getColumnType(column));
if (compare != 0)
return compare;
compare = ((Integer)rsmd1.getPrecision(column)).compareTo(rsmd2.getPrecision(column));
if (compare != 0)
return compare;
}
return 0;
}
catch (SQLException sqle) {
throw new MetaDataException("relational metadata information cannot be compared because of the following SQL exception : " + sqle.getMessage());
}
}
};
/**
* This constant provides a converter for relational metadata (result set
* metadata). All information of a given relational metadata is serialized
* to the specified output steam. The converter creates stored column
* metadata objects and puts them together to a result set metadata object.
*/
public static final Converter<ResultSetMetaData> RESULTSET_METADATA_CONVERTER = new Converter<ResultSetMetaData>() {
@Override
public ResultSetMetaData read(DataInput dataInput, ResultSetMetaData rsmd) throws IOException {
int columnCount = dataInput.readInt();
StoredColumnMetaData[] columnMetaData = new StoredColumnMetaData[columnCount];
for (int column = 1; column <= columnCount; column++)
columnMetaData[column-1] = new StoredColumnMetaData(
dataInput.readBoolean(),
dataInput.readBoolean(),
dataInput.readBoolean(),
dataInput.readBoolean(),
dataInput.readInt(),
dataInput.readBoolean(),
dataInput.readInt(),
dataInput.readUTF(),
dataInput.readUTF(),
dataInput.readUTF(),
dataInput.readInt(),
dataInput.readInt(),
dataInput.readUTF(),
dataInput.readUTF(),
dataInput.readInt(),
dataInput.readUTF(),
dataInput.readBoolean(),
dataInput.readBoolean(),
dataInput.readBoolean(),
dataInput.readUTF()
);
return new ColumnMetaDataResultSetMetaData(columnMetaData);
}
@Override
public void write(DataOutput dataOutput, ResultSetMetaData rsmd) throws IOException {
try {
dataOutput.writeInt(rsmd.getColumnCount());
for (int column = 1; column <= rsmd.getColumnCount(); column++) {
dataOutput.writeBoolean(rsmd.isAutoIncrement(column));
dataOutput.writeBoolean(rsmd.isCaseSensitive(column));
dataOutput.writeBoolean(rsmd.isSearchable(column));
dataOutput.writeBoolean(rsmd.isCurrency(column));
dataOutput.writeInt(rsmd.isNullable(column));
dataOutput.writeBoolean(rsmd.isSigned(column));
dataOutput.writeInt(rsmd.getColumnDisplaySize(column));
dataOutput.writeUTF(rsmd.getColumnLabel(column));
dataOutput.writeUTF(rsmd.getColumnName(column));
dataOutput.writeUTF(rsmd.getSchemaName(column));
dataOutput.writeInt(rsmd.getPrecision(column));
dataOutput.writeInt(rsmd.getScale(column));
dataOutput.writeUTF(rsmd.getTableName(column));
dataOutput.writeUTF(rsmd.getCatalogName(column));
dataOutput.writeInt(rsmd.getColumnType(column));
dataOutput.writeUTF(rsmd.getColumnTypeName(column));
dataOutput.writeBoolean(rsmd.isReadOnly(column));
dataOutput.writeBoolean(rsmd.isWritable(column));
dataOutput.writeBoolean(rsmd.isDefinitelyWritable(column));
dataOutput.writeUTF(rsmd.getColumnClassName(column));
}
}
catch (SQLException sqle) {
throw new IOException("relational metadata information cannot be serializied because of the following SQL exception : " + sqle.getMessage());
}
}
};
/**
* This constant provides a hash function for relational metadata (result
* set metadata). The hash-code of a given relational metadata is defined as
* the sum of number of columns, the hash-codes of the column names, the
* column types and a the precision.
*/
public static final Function<ResultSetMetaData, Integer> RESULTSET_METADATA_HASH_FUNCTION = new AbstractFunction<ResultSetMetaData, Integer>() {
@Override
public Integer invoke(ResultSetMetaData rsmd) {
try {
int hashCode = 0;
for (int column = 1; column <= rsmd.getColumnCount(); column++)
hashCode += 1 + rsmd.getColumnName(column).hashCode() + rsmd.getColumnType(column) + rsmd.getPrecision(column);
return hashCode;
}
catch (SQLException sqle) {
throw new MetaDataException("relational metadata information cannot be accessed because of the following SQL exception : " + sqle.getMessage());
}
}
};
/**
* This constant can be used to indicate that a column of the described
* result set stores the start timestamp of the tuple's time interval.
*/
public static final String TIME_INTERVAL_START_TIMESTAMP = "TIME_INTERVAL_START_TIMESTAMP";
/**
* This constant can be used to indicate that a column of the described
* result set stores the end timestamp of the tuple's time interval.
*/
public static final String TIME_INTERVAL_END_TIMESTAMP = "TIME_INTERVAL_END_TIMESTAMP";
/**
* This constant can be used to identify relational metadata inside a
* composite metadata.
*/
public static final String RESULTSET_METADATA_TYPE = "TABLE_METADATA";
/**
* Returns the metadata fragment of the given metadata provider's metadata
* representing its relational metadata.
*
* @param metaDataProvider the metadata provider containing the desired
* relational metadata fragment.
* @return the relational metadata fragment of the given metadata provider's
* metadata.
* @throws MetaDataException when the given metadata provider's metadata
* does not contain any relational metadata fragment.
*/
public static ResultSetMetaData getResultSetMetaData(MetaDataProvider<? extends CompositeMetaData<? super String, ? extends Object>> metaDataProvider) throws MetaDataException {
return (ResultSetMetaData)MetaDataProviders.getMetaDataFragment(metaDataProvider, RESULTSET_METADATA_TYPE);
}
/**
* Tests whether the columns of the the specified relational metadata have
* unique names.
*
* @param resultSetMetaData the relational metadata that's column names
* should be tested for uniqueness.
* @return <tt>true</tt>, if all columns of the specified relational
* metadata have unique names, otherwise <tt>false</tt>.
* @throws MetaDataException when the column names of the specified
* relational metadata cannot be accessed.
*/
public static boolean hasUniqueColumnNames(ResultSetMetaData resultSetMetaData) throws MetaDataException {
try {
HashSet<String> hashSet = new HashSet<String>();
String columnName;
for (int j = 1; j <= resultSetMetaData.getColumnCount(); j++) {
columnName = resultSetMetaData.getColumnName(j);
if (hashSet.contains(columnName))
return false;
hashSet.add(columnName);
}
return true;
}
catch (SQLException sqle) {
throw new MetaDataException("meta data cannot be accessed due to the following sql exception: " + sqle.getMessage());
}
}
/**
* Resolves the number of the column with the specified name in the given
* relational metadata. When no column name matches the given name
* (ignoring its case) 0 is returned. When multiple columns matches the
* given column name (ignoring its case) -1 is returned.
*
* @param resultSetMetaData the relational metadata that is searched for
* the given column name.
* @param columnName the column name which column number in the given
* relational metadata should be resolved.
* @return the number of the column with the specified name in the given
* relational metadata.
* @throws SQLException if a database access error occurs.
*/
public static int getColumnIndex(ResultSetMetaData resultSetMetaData, String columnName) throws SQLException {
int columnIndex = 0;
for (int column = 1; column <= resultSetMetaData.getColumnCount(); column++)
if (columnName.equalsIgnoreCase(resultSetMetaData.getColumnName(column)))
if (columnIndex == 0)
columnIndex = column;
else
return -1;
return columnIndex;
}
/**
* Transfers an array of column names into an array of indices. For every
* column, a partner is searched in the
* {@link java.sql.ResultSetMetaData result set's metadata}. When no column
* name matches the given name (ignoring its case) 0 is inserted into the
* array to be returned.
*
* @param resultSetMetaData the relational metadata that is searched for
* the given column names.
* @param columnNames an array of strings that contains the names of some
* of the result set's columns.
* @return an array of int values containing the indices of the given
* columns.
* @throws SQLException if a database access error occurs.
*/
public static int[] getColumnIndices(ResultSetMetaData resultSetMetaData, String[] columnNames) throws SQLException {
int[] columnIndices = new int[columnNames.length];
for (int i = 0; i < columnIndices.length; i++)
columnIndices[i] = getColumnIndex(resultSetMetaData, columnNames[i]);
return columnIndices;
}
/**
* Resolves the number of the column with the specified table and column
* name in the given relational metadata. When no column matches the given
* names (ignoring its case) 0 is returned. When multiple columns matches
* the given names (ignoring its case) -1 is returned.
*
* @param resultSetMetaData the relational metadata that is searched for
* the given column name.
* @param tableName the table name of the column whose column number in the
* given relational metadata should be resolved.
* @param columnName the name of the column whose column number in the
* given relational metadata should be resolved.
* @return the number of the column with the specified table and column
* name in the given relational metadata.
* @throws SQLException if a database access error occurs.
*/
public static int getColumnIndex(ResultSetMetaData resultSetMetaData, String tableName, String columnName) throws SQLException {
int columnIndex = 0;
for (int column = 1; column <= resultSetMetaData.getColumnCount(); column++)
if (tableName.equalsIgnoreCase(resultSetMetaData.getTableName(column)) && columnName.equalsIgnoreCase(resultSetMetaData.getColumnName(column)))
if (columnIndex == 0)
columnIndex = column;
else
return -1;
return columnIndex;
}
/**
* Resolves the indices of the columns with the specified table name in the
* given relational metadata.
*
* @param resultSetMetaData the relational metadata that is searched for
* columns matching the given table name.
* @param tableName the table name of the columns thats indices in the
* given relational metadata should be resolved.
* @return the indices of the columns with the specified table name in the
* given relational metadata.
* @throws SQLException if a database access error occurs.
*/
public static List<Integer> getColumnIndices(ResultSetMetaData resultSetMetaData, String tableName) throws SQLException {
List<Integer> columnIndices = new LinkedList<Integer>();
for (int column = 1; column <= resultSetMetaData.getColumnCount(); column++)
if (tableName.equalsIgnoreCase(resultSetMetaData.getTableName(column)))
columnIndices.add(column);
return columnIndices;
}
/**
* Resolves the number of the column with the specified schema, table and
* column name in the given relational metadata. When no column matches the
* given names (ignoring its case) 0 is returned.
*
* @param resultSetMetaData the relational metadata that is searched for
* the given column name.
* @param schemaName the schema name of the column whose column number in
* the given relational metadata should be resolved.
* @param tableName the table name of the column whose column number in the
* given relational metadata should be resolved.
* @param columnName the name of the column whose column number in the
* given relational metadata should be resolved.
* @return the number of the column with the specified schema, table and
* column name in the given relational metadata.
* @throws SQLException if a database access error occurs.
*/
public static int getColumnIndex(ResultSetMetaData resultSetMetaData, String schemaName, String tableName, String columnName) throws SQLException {
for (int column = 1; column <= resultSetMetaData.getColumnCount(); column++)
if (schemaName.equalsIgnoreCase(resultSetMetaData.getSchemaName(column)) && tableName.equalsIgnoreCase(resultSetMetaData.getTableName(column)) && columnName.equalsIgnoreCase(resultSetMetaData.getColumnName(column)))
return column;
return 0;
}
/**
* Resolves the indices of the columns with the specified schema and table
* name in the given relational metadata.
*
* @param resultSetMetaData the relational metadata that is searched for
* columns matching the given schema and table name.
* @param schemaName the schema name of the columns thats indices in the
* given relational metadata should be resolved.
* @param tableName the table name of the columns thats indices in the
* given relational metadata should be resolved.
* @return the indices of the columns with the specified schema and table
* name in the given relational metadata.
* @throws SQLException if a database access error occurs.
*/
public static List<Integer> getColumnIndices(ResultSetMetaData resultSetMetaData, String schemaName, String tableName) throws SQLException {
List<Integer> columnIndices = new LinkedList<Integer>();
for (int column = 1; column <= resultSetMetaData.getColumnCount(); column++)
if (schemaName.equalsIgnoreCase(resultSetMetaData.getSchemaName(column)) && tableName.equalsIgnoreCase(resultSetMetaData.getTableName(column)))
columnIndices.add(column);
return columnIndices;
}
/**
/**
* Transfers an array of indices into an array of column names. To get the
* column names, this method uses a result set 's metadata that is passed
* to the method call.
*
* @param resultSetMetaData the relational metadata that is searched for
* the given column indices.
* @param columnIndices an array of int values that contains indices of
* some of the result set's columns.
* @return an array of string objects containing the names of the given
* columns.
* @throws SQLException if a database access error occurs.
*/
public static String[] getColumnNames(ResultSetMetaData resultSetMetaData, int... columnIndices) throws SQLException {
String[] columnNames = new String[columnIndices.length];
for (int i = 0; i < columnNames.length; i++)
columnNames[i] = resultSetMetaData.getColumnName(columnIndices[i]);
return columnNames;
}
/**
* Get the columns numbers of strings in a metadata object.
*
* @param resultSetMetaData the relational metadata that is searched for
* string columns.
* @return a new array of the appropriate length.
*/
public static int[] getStringColumns(ResultSetMetaData resultSetMetaData) {
int[] columns = null;
int count = 0;
try {
columns = new int[resultSetMetaData.getColumnCount()];
for (int i = 1; i <= resultSetMetaData.getColumnCount(); i++)
if (resultSetMetaData.getColumnClassName(i).equals("java.lang.String"))
columns[count++] = i;
}
catch (SQLException e) {
// just ignore SQL exceptions when trying to find string columns
}
// construct a new array of the appropriate length
return Arrays.copy(columns, 0, count);
}
/**
* The default constructor has private access in order to ensure
* non-instantiability.
*/
private ResultSetMetaDatas() {
// private access in order to ensure non-instantiability
}
}