/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jkiss.dbeaver.model.impl.jdbc.exec;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.model.DBPDataKind;
import org.jkiss.dbeaver.model.DBPDataSource;
import org.jkiss.dbeaver.model.DBPDataTypeProvider;
import org.jkiss.dbeaver.model.DBUtils;
import org.jkiss.dbeaver.model.exec.DBCAttributeMetaData;
import org.jkiss.dbeaver.model.exec.DBCExecutionSource;
import org.jkiss.dbeaver.model.exec.DBCStatement;
import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.sql.SQLDataSource;
import org.jkiss.dbeaver.model.sql.SQLDialect;
import org.jkiss.dbeaver.model.struct.DBSDataType;
import org.jkiss.utils.CommonUtils;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
/**
* JDBCColumnMetaData
*/
public class JDBCColumnMetaData implements DBCAttributeMetaData {
private static final Log log = Log.getLog(JDBCColumnMetaData.class);
public static final String PROP_CATEGORY_COLUMN = "Column";
private final int ordinalPosition;
private boolean notNull;
private long displaySize;
private final String label;
private final String name;
private int precision;
private int scale;
private final String tableName;
private final int typeID;
private String typeName;
private boolean readOnly;
private boolean writable;
private boolean sequence;
private final DBPDataKind dataKind;
private JDBCTableMetaData tableMetaData;
private DBCExecutionSource source;
private String catalogName;
private String schemaName;
public JDBCColumnMetaData(JDBCResultSetMetaDataImpl resultSetMeta, int ordinalPosition)
throws SQLException
{
this(resultSetMeta.getResultSet().getSession().getDataSource(), resultSetMeta, ordinalPosition);
DBCStatement rsSource = resultSetMeta.getResultSet().getSourceStatement();
this.source = rsSource != null ? rsSource.getStatementSource() : null;
if (!CommonUtils.isEmpty(this.tableName)) {
this.tableMetaData = resultSetMeta.getTableMetaData(catalogName, schemaName, tableName);
} else {
this.tableMetaData = null;
}
if (this.tableMetaData != null) {
this.tableMetaData.addAttribute(this);
}
}
public JDBCColumnMetaData(DBPDataSource dataSource, ResultSetMetaData resultSetMeta, int ordinalPosition)
throws SQLException
{
this.ordinalPosition = ordinalPosition;
this.label = resultSetMeta.getColumnLabel(ordinalPosition + 1);
this.name = resultSetMeta.getColumnName(ordinalPosition + 1);
// TODO: some drivers (DB2) always mark all columns as read only. Dunno why. So let's ignore this property
// read-only connections are detected separately.
this.readOnly = false;//resultSetMeta.isReadOnly(ordinalPosition + 1);
try {
this.writable = resultSetMeta.isWritable(ordinalPosition + 1);
} catch (SQLException e) {
log.debug("Can't get column writable flag: " + e.getMessage());
}
String fetchedTableName = null;
try {
fetchedTableName = resultSetMeta.getTableName(ordinalPosition + 1);
} catch (SQLException e) {
log.debug("Can't get column table name: " + e.getMessage());
}
try {
catalogName = resultSetMeta.getCatalogName(ordinalPosition + 1);
} catch (SQLException e) {
log.debug("Can't get column catalog name: " + e.getMessage());
}
try {
schemaName = resultSetMeta.getSchemaName(ordinalPosition + 1);
} catch (SQLException e) {
log.debug("Can't get column schema name: " + e.getMessage());
}
// Check for tables name
// Sometimes [DBSPEC: Informix] it contains schema/catalog name inside
if (!CommonUtils.isEmpty(fetchedTableName) && CommonUtils.isEmpty(catalogName) && CommonUtils.isEmpty(schemaName)) {
if (dataSource instanceof SQLDataSource) {
SQLDialect sqlDialect = ((SQLDataSource) dataSource).getSQLDialect();
if (!DBUtils.isQuotedIdentifier(dataSource, fetchedTableName)) {
final String catalogSeparator = sqlDialect.getCatalogSeparator();
final int catDivPos = fetchedTableName.indexOf(catalogSeparator);
if (catDivPos != -1 && (sqlDialect.getCatalogUsage() & SQLDialect.USAGE_DML) != 0) {
// Catalog in table name - extract it
catalogName = fetchedTableName.substring(0, catDivPos);
fetchedTableName = fetchedTableName.substring(catDivPos + catalogSeparator.length());
}
final char structSeparator = sqlDialect.getStructSeparator();
final int schemaDivPos = fetchedTableName.indexOf(structSeparator);
if (schemaDivPos != -1 && (sqlDialect.getSchemaUsage() & SQLDialect.USAGE_DML) != 0) {
// Schema in table name - extract it
schemaName = fetchedTableName.substring(0, schemaDivPos);
fetchedTableName = fetchedTableName.substring(schemaDivPos + 1);
}
}
}
}
try {
this.notNull = resultSetMeta.isNullable(ordinalPosition + 1) == ResultSetMetaData.columnNoNulls;
} catch (SQLException e) {
this.notNull = false;
log.debug("Can't get column nullability: " + e.getMessage());
}
try {
this.displaySize = resultSetMeta.getColumnDisplaySize(ordinalPosition + 1);
} catch (SQLException e) {
this.displaySize = 0;
}
try {
this.typeName = resultSetMeta.getColumnTypeName(ordinalPosition + 1);
} catch (SQLException e) {
log.debug("Can't get column type name: " + e.getMessage());
this.typeName = "unknown";
}
{
int typeID = resultSetMeta.getColumnType(ordinalPosition + 1);
DBPDataKind dataKind = null;
if (dataSource instanceof DBPDataTypeProvider) {
DBSDataType dataType = ((DBPDataTypeProvider) dataSource).getLocalDataType(typeName);
if (dataType != null) {
typeID = dataType.getTypeID();
dataKind = dataType.getDataKind();
}
}
if (dataKind == null) {
dataKind = JDBCUtils.resolveDataKind(dataSource, typeName, typeID);
}
this.typeID = typeID;
this.dataKind = dataKind;
}
try {
this.sequence = resultSetMeta.isAutoIncrement(ordinalPosition + 1);
} catch (SQLException e) {
this.sequence = false;
log.debug("Can't get column auto increment: " + e.getMessage());
}
try {
this.precision = resultSetMeta.getPrecision(ordinalPosition + 1);
} catch (Exception e) {
// NumberFormatException occurred in Oracle on BLOB columns
this.precision = 0;
}
try {
this.scale = resultSetMeta.getScale(ordinalPosition + 1);
} catch (Exception e) {
this.scale = 0;
}
this.tableName = fetchedTableName;
}
@Override
public DBCExecutionSource getSource() {
return source;
}
@NotNull
@Property(viewable = true, category = PROP_CATEGORY_COLUMN, order = 1)
@Override
public String getName() {
return name;
}
@Property(viewable = true, category = PROP_CATEGORY_COLUMN, order = 2)
@NotNull
@Override
public String getLabel() {
return label;
}
@Property(viewable = true, category = PROP_CATEGORY_COLUMN, order = 3)
@Override
public int getOrdinalPosition() {
return ordinalPosition;
}
@Nullable
@Property(viewable = true, category = PROP_CATEGORY_COLUMN, order = 4)
@Override
public String getEntityName() {
return tableName;
}
@Property(viewable = true, category = PROP_CATEGORY_COLUMN, order = 30)
@Override
public boolean isRequired() {
return notNull;
}
@Property(viewable = true, category = PROP_CATEGORY_COLUMN, order = 30)
@Override
public boolean isAutoGenerated() {
return sequence;
}
@Property(viewable = true, category = PROP_CATEGORY_COLUMN, order = 20)
@Override
public long getMaxLength() {
return displaySize;
}
@Property(viewable = true, category = PROP_CATEGORY_COLUMN, order = 21)
@Override
public int getPrecision() {
return precision;
}
@Property(viewable = true, category = PROP_CATEGORY_COLUMN, order = 22)
@Override
public int getScale() {
return scale;
}
@Override
public int getTypeID() {
return typeID;
}
@Override
public DBPDataKind getDataKind() {
return dataKind;
}
@Property(viewable = true, category = PROP_CATEGORY_COLUMN, order = 4)
@Override
public String getTypeName() {
return typeName;
}
@Override
public String getFullTypeName() {
return DBUtils.getFullTypeName(this);
}
@Override
public boolean isReadOnly() {
return readOnly;
}
public boolean isWritable() {
return writable;
}
@Nullable
@Override
public JDBCTableMetaData getEntityMetaData() {
return tableMetaData;
}
@Override
public String toString() {
StringBuilder db = new StringBuilder();
if (!CommonUtils.isEmpty(tableName)) {
db.append(tableName).append('.');
}
if (!CommonUtils.isEmpty(name)) {
db.append(name);
}
if (!CommonUtils.isEmpty(label)) {
db.append(" as ").append(label);
}
return db.toString();
}
}