/*
* 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.ext.postgresql.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.postgresql.PostgreUtils;
import org.jkiss.dbeaver.model.DBPDataKind;
import org.jkiss.dbeaver.model.DBPHiddenObject;
import org.jkiss.dbeaver.model.DBPNamedObject2;
import org.jkiss.dbeaver.model.DBUtils;
import org.jkiss.dbeaver.model.exec.jdbc.JDBCResultSet;
import org.jkiss.dbeaver.model.impl.DBPositiveNumberTransformer;
import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils;
import org.jkiss.dbeaver.model.impl.jdbc.struct.JDBCTableColumn;
import org.jkiss.dbeaver.model.meta.IPropertyValueListProvider;
import org.jkiss.dbeaver.model.meta.IPropertyValueTransformer;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.sql.SQLUtils;
import org.jkiss.dbeaver.model.struct.DBSEntity;
import org.jkiss.dbeaver.model.struct.DBSTypedObjectEx;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
/**
* PostgreAttribute
*/
public abstract class PostgreAttribute<OWNER extends DBSEntity & PostgreObject> extends JDBCTableColumn<OWNER> implements DBSTypedObjectEx, DBPNamedObject2, DBPHiddenObject
{
private static final Log log = Log.getLog(PostgreAttribute.class);
@NotNull
private PostgreDataType dataType;
private String comment;
private long charLength;
private int arrayDim;
private int inheritorsCount;
private String description;
protected PostgreAttribute(
OWNER table)
{
super(table, false);
}
public PostgreAttribute(
OWNER table,
JDBCResultSet dbResult)
throws DBException
{
super(table, true);
loadInfo(dbResult);
}
public PostgreDatabase getDatabase() {
return getTable().getDatabase();
}
private void loadInfo(JDBCResultSet dbResult)
throws DBException
{
setName(JDBCUtils.safeGetString(dbResult, "attname"));
setOrdinalPosition(JDBCUtils.safeGetInt(dbResult, "attnum"));
setRequired(JDBCUtils.safeGetBoolean(dbResult, "attnotnull"));
final long typeId = JDBCUtils.safeGetLong(dbResult, "atttypid");
dataType = getTable().getDatabase().getDataType(typeId);
if (dataType == null) {
throw new DBException("Attribute data type '" + typeId + "' not found");
}
setTypeName(dataType.getTypeName());
setValueType(dataType.getTypeID());
setDefaultValue(JDBCUtils.safeGetString(dbResult, "def_value"));
int typeMod = JDBCUtils.safeGetInt(dbResult, "atttypmod");
int maxLength = PostgreUtils.getAttributePrecision(typeId, typeMod);
DBPDataKind dataKind = dataType.getDataKind();
if (dataKind == DBPDataKind.NUMERIC || dataKind == DBPDataKind.DATETIME) {
setMaxLength(0);
} else {
if (maxLength <= 0) {
maxLength = PostgreUtils.getDisplaySize(typeId, typeMod);
}
if (maxLength >= 0) {
setMaxLength(maxLength);
} else {
// TypeMod can be anything.
// It is often used in packed format and has no numeric meaning at all
//setMaxLength(typeMod);
}
}
setPrecision(maxLength);
setScale(PostgreUtils.getScale(typeId, typeMod));
this.description = JDBCUtils.safeGetString(dbResult, "description");
this.arrayDim = JDBCUtils.safeGetInt(dbResult, "attndims");
this.inheritorsCount = JDBCUtils.safeGetInt(dbResult, "attinhcount");
setPersisted(true);
}
@NotNull
@Override
public PostgreDataSource getDataSource()
{
return getTable().getDataSource();
}
@NotNull
@Override
@Property(viewable = true, editable = true, updatable = true, order = 20, listProvider = DataTypeListProvider.class, valueTransformer = DataTypeValueTransformer.class)
public PostgreDataType getDataType() {
return dataType;
}
public void setDataType(@NotNull PostgreDataType dataType) {
this.dataType = dataType;
setTypeName(dataType.getTypeName());
setValueType(dataType.getTypeID());
}
@Override
public DBPDataKind getDataKind() {
return dataType.getDataKind();
}
@Override
@Property(viewable = true, editable = true, updatable = true, valueRenderer = DBPositiveNumberTransformer.class, order = 21)
public long getMaxLength()
{
return super.getMaxLength();
}
@Override
public String getTypeName()
{
return dataType.getTypeName();
}
@Override
@Property(viewable = true, editable = true, updatable = true, valueRenderer = DBPositiveNumberTransformer.class, order = 22)
public int getPrecision()
{
return super.getPrecision();
}
@Override
@Property(viewable = true, editable = true, updatable = true, valueRenderer = DBPositiveNumberTransformer.class, order = 23)
public int getScale()
{
return super.getScale();
}
@Override
@Property(viewable = true, editable = true, updatable = true, order = 50)
public boolean isRequired()
{
return super.isRequired();
}
@Override
public boolean isAutoGenerated()
{
final String def = getDefaultValue();
return def != null && def.contains("nextval(");
}
@Override
@Property(viewable = true, editable = true, updatable = true, order = 70)
public String getDefaultValue()
{
return super.getDefaultValue();
}
@Nullable
@Override
@Property(viewable = true, editable = true, updatable = true, order = 100)
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
@Override
public boolean isHidden() {
return isPersisted() && getOrdinalPosition() < 0;
}
public String getFullTypeName() {
String fqtn = dataType.getTypeName();
if (dataType.getDataKind() != DBPDataKind.CONTENT) {
return DBUtils.getFullTypeName(this);
}
return fqtn;
}
public static class DataTypeListProvider implements IPropertyValueListProvider<PostgreAttribute> {
@Override
public boolean allowCustomValue()
{
return true;
}
@Override
public Object[] getPossibleValues(PostgreAttribute column)
{
Set<PostgreDataType> types = new TreeSet<>(new Comparator<PostgreDataType>() {
@Override
public int compare(PostgreDataType o1, PostgreDataType o2) {
return o1.getTypeName().compareTo(o2.getTypeName());
}
});
for (PostgreDataType type : column.getDataSource().getLocalDataTypes()) {
types.add(type);
}
return types.toArray(new PostgreDataType[types.size()]);
}
}
public static class DataTypeValueTransformer implements IPropertyValueTransformer<PostgreAttribute, Object> {
@Override
public PostgreDataType transform(PostgreAttribute object, Object value) {
if (value instanceof String) {
PostgreDataType dataType = object.getDataSource().getDefaultInstance().getDataType((String) value);
if (dataType == null) {
throw new IllegalArgumentException("Bad data type name specified: " + value);
}
return dataType;
} else if (value instanceof PostgreDataType) {
return (PostgreDataType) value;
} else {
throw new IllegalArgumentException("Invalid type value: " + value);
}
}
}
}