/*
* DBeaver - Universal Database Manager
* Copyright (C) 2013-2015 Denis Forveille (titou10.titou10@gmail.com)
* 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.ext.db2.model;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.ext.db2.DB2Constants;
import org.jkiss.dbeaver.ext.db2.DB2Utils;
import org.jkiss.dbeaver.ext.db2.model.dict.DB2DataTypeMetaType;
import org.jkiss.dbeaver.ext.db2.model.dict.DB2OwnerType;
import org.jkiss.dbeaver.ext.db2.model.module.DB2Module;
import org.jkiss.dbeaver.model.DBPDataKind;
import org.jkiss.dbeaver.model.DBPEvaluationContext;
import org.jkiss.dbeaver.model.DBPQualifiedObject;
import org.jkiss.dbeaver.model.DBUtils;
import org.jkiss.dbeaver.model.exec.DBCException;
import org.jkiss.dbeaver.model.exec.DBCLogicalOperator;
import org.jkiss.dbeaver.model.impl.DBObjectNameCaseTransformer;
import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
import org.jkiss.dbeaver.model.struct.DBSDataType;
import org.jkiss.dbeaver.model.struct.DBSObject;
import org.jkiss.dbeaver.model.struct.DBSTypedObject;
import org.jkiss.utils.CommonUtils;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.HashMap;
import java.util.Map;
/**
* DB2 data types
*
* @author Denis Forveille
*/
public class DB2DataType extends DB2Object<DBSObject> implements DBSDataType, DBPQualifiedObject {
private static final Log LOG = Log.getLog(DB2DataType.class);
private static final Map<String, TypeDesc> PREDEFINED_TYPES = new HashMap<>(32); // See init below
private DBSObject parentNode; // see below
private DB2Schema db2Schema;
private String fullyQualifiedName;
private TypeDesc typeDesc;
private Integer db2TypeId;
private String ownerCol;
private DB2OwnerType ownerType;
private String sourceSchemaName;
private String sourceModuleName;
private String sourceName;
private DB2DataTypeMetaType metaType;
private Integer length;
private Integer scale;
private Timestamp createTime;
private Timestamp alterTime;
private Timestamp lastRegenTime;
private String constraintText;
private String remarks;
private DB2Module db2Module;
// -----------------------
// Constructors
// -----------------------
public DB2DataType(DBSObject owner, ResultSet dbResult) throws DBException
{
super(owner, JDBCUtils.safeGetStringTrimmed(dbResult, "TYPENAME"), true);
DB2DataSource db2DataSource = (DB2DataSource) owner.getDataSource();
this.db2TypeId = JDBCUtils.safeGetInteger(dbResult, "TYPEID");
this.ownerCol = JDBCUtils.safeGetString(dbResult, "OWNER");
this.sourceSchemaName = JDBCUtils.safeGetStringTrimmed(dbResult, "SOURCESCHEMA");
this.sourceName = JDBCUtils.safeGetString(dbResult, "SOURCENAME");
this.metaType = CommonUtils.valueOf(DB2DataTypeMetaType.class, JDBCUtils.safeGetString(dbResult, "METATYPE"));
this.length = JDBCUtils.safeGetInteger(dbResult, "LENGTH");
this.scale = JDBCUtils.safeGetInteger(dbResult, "SCALE");
this.createTime = JDBCUtils.safeGetTimestamp(dbResult, "CREATE_TIME");
this.alterTime = JDBCUtils.safeGetTimestamp(dbResult, "ALTER_TIME");
this.remarks = JDBCUtils.safeGetString(dbResult, "REMARKS");
if (db2DataSource.isAtLeastV9_5()) {
this.ownerType = CommonUtils.valueOf(DB2OwnerType.class, JDBCUtils.safeGetString(dbResult, "OWNERTYPE"));
}
if (db2DataSource.isAtLeastV9_7()) {
this.sourceModuleName = JDBCUtils.safeGetStringTrimmed(dbResult, "SOURCEMODULENAME");
}
if (db2DataSource.isAtLeastV10_5()) {
this.lastRegenTime = JDBCUtils.safeGetTimestamp(dbResult, "LAST_REGEN_TIME");
this.constraintText = JDBCUtils.safeGetString(dbResult, "CONSTRAINT_TEXT");
}
// Store associated DB2Schema
// DB2DataType can have 3 owners:
// - DataSource (= "System" DataTypes)
// - DB2Schema (=UDT)
// - DB2Module
if (owner instanceof DB2Schema) {
this.db2Schema = (DB2Schema) owner;
} else {
if (owner instanceof DB2Module) {
this.db2Schema = ((DB2Module) owner).getSchema();
String typeModuleName = JDBCUtils.safeGetStringTrimmed(dbResult, "TYPEMODULENAME");
if (typeModuleName != null) {
this.db2Module = DB2Utils.findModuleBySchemaNameAndName(new VoidProgressMonitor(), db2DataSource,
db2Schema.getName(), typeModuleName);
}
} else {
// System datatypes
String schemaName = JDBCUtils.safeGetStringTrimmed(dbResult, "TYPESCHEMA");
try {
this.db2Schema = db2DataSource.getSchema(new VoidProgressMonitor(), schemaName);
} catch (DBException e) {
LOG.error("Impossible! Schema '" + schemaName + "' for dataType '" + name + "' not found??", e);
// In this case, 'this.db2Schema' will be null...
}
}
}
if ((db2Schema != null) && (db2Schema.getName().equals(DB2Constants.SYSTEM_DATATYPE_SCHEMA))) {
// DF: not sure of that. Maybe for system DataTypes, we should set db2Schema to null instead..
fullyQualifiedName = name;
} else {
fullyQualifiedName = db2Schema.getName() + "." + name;
}
// Determine DBSKind and javax.sql.Types
TypeDesc tempTypeDesc = null;
// If the dataType is a SYSIBM dataType, get it
if (db2Schema.getName().equals(DB2Constants.SYSTEM_DATATYPE_SCHEMA)) {
tempTypeDesc = PREDEFINED_TYPES.get(name);
// NLS_STRING_UNITS_TYPE is a SYSIBM type, but not a predefined one...
// so tempTypeDesc may be null at this time even if the schema is SYSIBM
}
if (tempTypeDesc == null) {
// This is a UDT
// Check for Structured or Array like DataTypes
switch (metaType) {
case R:
tempTypeDesc = new TypeDesc(DBPDataKind.STRUCT, Types.STRUCT, null, null, null);
break;
case A:
case L:
tempTypeDesc = new TypeDesc(DBPDataKind.ARRAY, Types.ARRAY, null, null, null);
break;
default:
// If the UDT is based on a SYSIBM dataType, get it
if ((sourceSchemaName != null) && (sourceSchemaName.equals(DB2Constants.SYSTEM_DATATYPE_SCHEMA))) {
LOG.debug(name + " is a User Defined Type base on a System Data Type.");
tempTypeDesc = PREDEFINED_TYPES.get(sourceName);
} else {
// This UDT is based on another UDT, set it's TypeDesc to unkknown as looking for the base type recursively
// could lead to infinite loops:
// load corresponding module ->module load its own UDTs ->come back here to instanciate the UDT -> look for type
// in
// module etc.
// It would have to be done recursively with a direct SQL. No real benefit here..
LOG.debug(name + " is a User Defined Type base on another UDT. Set its DBPDataKind to UNKNOWN/OTHER");
tempTypeDesc = new TypeDesc(DBPDataKind.UNKNOWN, Types.OTHER, null, null, null);
}
break;
}
}
this.typeDesc = tempTypeDesc;
// if the getParentObject() return the "real" parent ie DB2Schema or DB2DataSource,
// when someone, as a first action, opens the table/column tab and then clicks on the datatype link,
// nothing is displayed and the following message appears in the logs :
// !MESSAGE Can't find tree node for object <database name> (org.jkiss.dbeaver.ext.db2.model.DB2DataSource)
// With this code (copied from OracleDataType), it works.
if ((parent instanceof DB2Schema) || (parent instanceof DB2Module)) {
parentNode = parent;
} else {
if (parent instanceof DB2DataSource) {
parentNode = ((DB2DataSource) parent).getContainer();
}
}
}
@Override
public DBSObject getParentObject()
{
return parentNode;
}
@Override
public String getTypeName()
{
return name;
}
@Override
public String getFullTypeName() {
return DBUtils.getFullTypeName(this);
}
@NotNull
@Override
public String getFullyQualifiedName(DBPEvaluationContext context)
{
return fullyQualifiedName;
}
public int getEquivalentSqlType()
{
return typeDesc.sqlType;
}
@Override
public int getPrecision()
{
if (typeDesc.precision != null) {
return typeDesc.precision;
} else {
return 0;
}
}
@Nullable
@Override
public Object geTypeExtension()
{
return metaType;
}
@Nullable
@Override
public DBSDataType getComponentType(@NotNull DBRProgressMonitor monitor) throws DBCException
{
return null;
}
@Override
public int getMinScale()
{
if (typeDesc.minScale != null) {
return typeDesc.minScale;
} else {
return 0;
}
}
@Override
public int getMaxScale()
{
if (typeDesc.maxScale != null) {
return typeDesc.maxScale;
} else {
return 0;
}
}
@NotNull
@Override
public DBCLogicalOperator[] getSupportedOperators(DBSTypedObject attribute)
{
return DBUtils.getDefaultOperators(this);
}
// -----------------
// Properties
// -----------------
@NotNull
@Override
@Property(viewable = true, editable = false, valueTransformer = DBObjectNameCaseTransformer.class, order = 1)
public String getName()
{
return name;
}
@Property(viewable = true, editable = false, order = 2)
public DB2Schema getSchema()
{
return db2Schema;
}
@Property(viewable = true, editable = false, order = 3)
public DB2Module getModule()
{
return db2Module;
}
@Override
@Property(viewable = true, editable = false, order = 4)
public DBPDataKind getDataKind()
{
return typeDesc == null ? DBPDataKind.UNKNOWN : typeDesc.dataKind;
}
@Property(viewable = false, editable = false, order = 5)
public DB2DataTypeMetaType getMetaType()
{
return metaType;
}
@Override
@Property(viewable = true, editable = false, order = 5)
public long getMaxLength()
{
return length;
}
@Override
@Property(viewable = true, editable = false, order = 6)
public int getScale()
{
return scale;
}
@Override
@Property(viewable = false, editable = false, order = 10)
public int getTypeID()
{
return typeDesc.sqlType;
}
@Property(viewable = false, editable = false, order = 11)
public Integer getDb2TypeId()
{
return db2TypeId;
}
@Property(viewable = false, editable = false)
public String getConstraintText()
{
return constraintText;
}
@Nullable
@Override
@Property(viewable = false, editable = false)
public String getDescription()
{
return remarks;
}
@Property(viewable = false, editable = false, category = DB2Constants.CAT_SOURCE, order = 20)
public String getSourceSchemaName()
{
return sourceSchemaName;
}
@Property(viewable = false, editable = false, category = DB2Constants.CAT_SOURCE, order = 21)
public String getSourceModuleName()
{
return sourceModuleName;
}
@Property(viewable = false, editable = false, category = DB2Constants.CAT_SOURCE, order = 22)
public String getSourceName()
{
return sourceName;
}
@Property(viewable = false, editable = false, category = DB2Constants.CAT_OWNER)
public String getOwner()
{
return ownerCol;
}
@Property(viewable = false, editable = false, category = DB2Constants.CAT_OWNER)
public DB2OwnerType getOwnerType()
{
return ownerType;
}
@Property(viewable = false, editable = false, category = DB2Constants.CAT_DATETIME)
public Timestamp getCreateTime()
{
return createTime;
}
@Property(viewable = false, editable = false, category = DB2Constants.CAT_DATETIME)
public Timestamp getAlterTime()
{
return alterTime;
}
@Property(viewable = false, editable = false, category = DB2Constants.CAT_DATETIME)
public Timestamp getLastRegenTime()
{
return lastRegenTime;
}
// --------------
// Helper Objects
// --------------
private static final class TypeDesc {
private final DBPDataKind dataKind;
private final Integer sqlType;
private final Integer precision;
private final Integer minScale;
private final Integer maxScale;
private TypeDesc(DBPDataKind dataKind, Integer sqlType, Integer precision, Integer minScale, Integer maxScale)
{
this.dataKind = dataKind;
this.sqlType = sqlType;
this.precision = precision;
this.minScale = minScale;
this.maxScale = maxScale;
}
}
static {
PREDEFINED_TYPES.put("ARRAY", new TypeDesc(DBPDataKind.ARRAY, Types.ARRAY, null, null, null));
PREDEFINED_TYPES.put("BIGINT", new TypeDesc(DBPDataKind.NUMERIC, Types.BIGINT, 20, 0, 0));
PREDEFINED_TYPES.put("BINARY", new TypeDesc(DBPDataKind.BINARY, Types.BINARY, 254, null, null));
PREDEFINED_TYPES.put("BLOB", new TypeDesc(DBPDataKind.CONTENT, Types.BLOB, 2147483647, null, null));
PREDEFINED_TYPES.put("BOOLEAN", new TypeDesc(DBPDataKind.BOOLEAN, Types.BOOLEAN, null, null, null));
PREDEFINED_TYPES.put("CHARACTER", new TypeDesc(DBPDataKind.STRING, Types.CHAR, 254, null, null));
PREDEFINED_TYPES.put("CLOB", new TypeDesc(DBPDataKind.CONTENT, Types.CLOB, 2147483647, null, null));
PREDEFINED_TYPES.put("DATE", new TypeDesc(DBPDataKind.DATETIME, Types.DATE, 10, null, null));
PREDEFINED_TYPES.put("DBCLOB", new TypeDesc(DBPDataKind.CONTENT, Types.CLOB, 1073741823, null, null));
PREDEFINED_TYPES.put("DECIMAL", new TypeDesc(DBPDataKind.NUMERIC, Types.DECIMAL, 31, 0, 31));
PREDEFINED_TYPES.put("DOUBLE", new TypeDesc(DBPDataKind.NUMERIC, Types.DOUBLE, 53, 0, 0));
PREDEFINED_TYPES.put("GRAPHIC", new TypeDesc(DBPDataKind.STRING, Types.CHAR, 127, null, null));
PREDEFINED_TYPES.put("INTEGER", new TypeDesc(DBPDataKind.NUMERIC, Types.INTEGER, 10, 0, 0));
PREDEFINED_TYPES.put("LONG VARCHAR", new TypeDesc(DBPDataKind.STRING, Types.LONGVARCHAR, 32700, null, null));
PREDEFINED_TYPES.put("LONG VARGRAPHIC", new TypeDesc(DBPDataKind.STRING, Types.LONGVARCHAR, 16350, null, null));
PREDEFINED_TYPES.put("REAL", new TypeDesc(DBPDataKind.NUMERIC, Types.REAL, 24, 0, 0));
PREDEFINED_TYPES.put("REFERENCE", new TypeDesc(DBPDataKind.REFERENCE, Types.REF, null, null, null));
PREDEFINED_TYPES.put("ROW", new TypeDesc(DBPDataKind.STRUCT, Types.ROWID, null, null, null));
PREDEFINED_TYPES.put("SMALLINT", new TypeDesc(DBPDataKind.NUMERIC, Types.SMALLINT, 5, 0, 0));
PREDEFINED_TYPES.put("TIME", new TypeDesc(DBPDataKind.DATETIME, Types.TIME, 8, 0, 0));
PREDEFINED_TYPES.put("TIMESTAMP", new TypeDesc(DBPDataKind.DATETIME, Types.TIMESTAMP, 32, 0, 12));
PREDEFINED_TYPES.put("VARBINARY", new TypeDesc(DBPDataKind.BINARY, Types.VARBINARY, 32762, null, null));
PREDEFINED_TYPES.put("VARCHAR", new TypeDesc(DBPDataKind.STRING, Types.VARCHAR, 4000, null, null));
PREDEFINED_TYPES.put("VARGRAPHIC", new TypeDesc(DBPDataKind.STRING, Types.VARCHAR, 16336, null, null));
PREDEFINED_TYPES.put("XML", new TypeDesc(DBPDataKind.CONTENT, Types.SQLXML, null, null, null));
PREDEFINED_TYPES.put("CURSOR", new TypeDesc(DBPDataKind.UNKNOWN, DB2Constants.EXT_TYPE_CURSOR, null, null, null));
PREDEFINED_TYPES.put(DB2Constants.TYPE_NAME_DECFLOAT,
new TypeDesc(DBPDataKind.NUMERIC, DB2Constants.EXT_TYPE_DECFLOAT, 34, 0, 0));
}
}