/*
* 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.data;
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.DBUtils;
import org.jkiss.dbeaver.model.data.DBDComposite;
import org.jkiss.dbeaver.model.data.DBDValue;
import org.jkiss.dbeaver.model.data.DBDValueCloneable;
import org.jkiss.dbeaver.model.exec.DBCException;
import org.jkiss.dbeaver.model.exec.DBCSession;
import org.jkiss.dbeaver.model.impl.jdbc.JDBCStructImpl;
import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils;
import org.jkiss.dbeaver.model.impl.struct.AbstractAttribute;
import org.jkiss.dbeaver.model.impl.struct.AbstractStructDataType;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
import org.jkiss.dbeaver.model.struct.*;
import org.jkiss.utils.CommonUtils;
import java.sql.*;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
/**
* abstract struct implementation.
*/
public abstract class JDBCComposite implements DBDComposite, DBDValueCloneable {
private static final Log log = Log.getLog(JDBCComposite.class);
@NotNull
protected DBSDataType type;
@NotNull
protected DBSEntityAttribute[] attributes;
@NotNull
protected Object[] values;
protected boolean modified;
protected JDBCComposite() {
}
protected JDBCComposite(@NotNull JDBCComposite struct, @NotNull DBRProgressMonitor monitor) throws DBCException {
this.type = struct.type;
this.attributes = Arrays.copyOf(struct.attributes, struct.attributes.length);
this.values = new Object[struct.values.length];
for (int i = 0; i < struct.values.length; i++) {
Object value = struct.values[i];
if (value instanceof DBDValueCloneable) {
value = ((DBDValueCloneable)value).cloneValue(monitor);
}
this.values[i] = value;
}
}
@Override
public boolean isNull()
{
for (Object value : values) {
if (!DBUtils.isNullValue(value)) {
return false;
}
}
return true;
}
@Override
public boolean isModified() {
return modified;
}
@Override
public void release()
{
values = EMPTY_VALUES;
}
@NotNull
public String getTypeName()
{
return type.getTypeName();
}
public String getStringRepresentation()
{
return getTypeName();
}
@NotNull
public Object[] getValues() {
return values;
}
public Struct getStructValue() throws DBCException {
Object[] attrs = new Object[values.length];
for (int i = 0; i < values.length; i++) {
Object attr = values[i];
if (attr instanceof DBDValue) {
attr = ((DBDValue) attr).getRawValue();
}
attrs[i] = attr;
}
final DBSDataType dataType = getDataType();
try (DBCSession session = DBUtils.openUtilSession(new VoidProgressMonitor(), dataType.getDataSource(), "Create JDBC struct")) {
if (session instanceof Connection) {
return ((Connection) session).createStruct(dataType.getTypeName(), attrs);
} else {
return new JDBCStructImpl(dataType.getTypeName(), attrs);
}
} catch (Throwable e) {
throw new DBCException("Error creating struct", e);
}
}
@Override
public DBSDataType getDataType()
{
return type;
}
@Override
public Struct getRawValue() {
try {
return getStructValue();
} catch (Throwable e) {
log.error(e);
return null;
}
}
@NotNull
@Override
public DBSAttributeBase[] getAttributes()
{
return attributes;
}
@Nullable
@Override
public Object getAttributeValue(@NotNull DBSAttributeBase attribute) throws DBCException
{
int position = attribute.getOrdinalPosition();
if (position >= values.length) {
log.debug("Attribute index is out of range (" + position + ">=" + values.length + ")");
return null;
}
return values[position];
}
@Override
public void setAttributeValue(@NotNull DBSAttributeBase attribute, @Nullable Object value) {
if (!CommonUtils.equalObjects(values[attribute.getOrdinalPosition()], value)) {
this.values[attribute.getOrdinalPosition()] = value;
this.modified = true;
}
}
protected class StructType extends AbstractStructDataType<DBPDataSource> implements DBSEntity {
public StructType(DBPDataSource dataSource) {
super(dataSource);
}
@NotNull
@Override
public String getTypeName() {
return "Object";
}
@Override
public int getTypeID() {
return Types.STRUCT;
}
@Override
public DBPDataKind getDataKind() {
return DBPDataKind.STRUCT;
}
@NotNull
@Override
public DBSEntityType getEntityType() {
return DBSEntityType.TYPE;
}
@Nullable
@Override
public Collection<? extends DBSEntityAttribute> getAttributes(@NotNull DBRProgressMonitor monitor) {
return Arrays.asList(attributes);
}
}
protected static class StructAttribute extends AbstractAttribute implements DBSEntityAttribute {
final DBSDataType type;
DBPDataKind dataKind;
public StructAttribute(DBSDataType type, int index, Object value)
{
this.type = type;
if (value instanceof CharSequence) {
dataKind = DBPDataKind.STRING;
setValueType(Types.VARCHAR);
} else if (value instanceof Number) {
dataKind = DBPDataKind.NUMERIC;
setValueType(Types.NUMERIC);
} else if (value instanceof Boolean) {
dataKind = DBPDataKind.BOOLEAN;
setValueType(Types.BOOLEAN);
} else if (value instanceof Date) {
dataKind = DBPDataKind.DATETIME;
setValueType(Types.TIMESTAMP);
} else if (value instanceof byte[]) {
dataKind = DBPDataKind.BINARY;
setValueType(Types.BINARY);
} else {
dataKind = DBPDataKind.OBJECT;
setValueType(Types.OTHER);
}
setName("Attr" + index);
setOrdinalPosition(index);
setTypeName(dataKind.name());
}
public StructAttribute(DBSDataType type, ResultSetMetaData metaData, int index) throws SQLException
{
super(
metaData.getColumnName(index + 1),
metaData.getColumnTypeName(index + 1),
metaData.getColumnType(index + 1),
index,
metaData.getColumnDisplaySize(index + 1),
metaData.getScale(index + 1),
metaData.getPrecision(index + 1),
metaData.isNullable(index + 1) == ResultSetMetaData.columnNoNulls,
metaData.isAutoIncrement(index + 1));
this.type = type;
dataKind = JDBCUtils.resolveDataKind(type.getDataSource(), getTypeName(), getTypeID());
}
@Override
public DBPDataKind getDataKind()
{
return dataKind;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof StructAttribute)) {
return false;
}
StructAttribute attr = (StructAttribute)obj;
return CommonUtils.equalObjects(name, attr.name) &&
valueType == attr.valueType &&
maxLength == attr.maxLength &&
scale == attr.scale &&
precision == attr.precision &&
CommonUtils.equalObjects(typeName, attr.typeName) &&
ordinalPosition == attr.ordinalPosition;
}
@Override
public int hashCode() {
return (int) (name.hashCode() + valueType + maxLength + scale + precision + typeName.hashCode() + ordinalPosition);
}
@Nullable
@Override
public String getDefaultValue() {
return null;
}
@NotNull
@Override
public DBSEntity getParentObject() {
return (StructType) type;
}
@NotNull
@Override
public DBPDataSource getDataSource() {
return type.getDataSource();
}
}
/*
private String makeStructString() throws SQLException
{
StringBuilder str = new StringBuilder(200);
String typeName = getTypeName();
str.append(typeName);
str.append("(");
int i = 0;
for (int i1 = 0; i1 < attributes.length; i1++) {
DBSEntityAttribute attr = attributes[i1];
Object item = values[i];
if (i > 0) str.append(",");
//str.append(entry.getKey().getName()).append(':');
if (DBUtils.isNullValue(item)) {
str.append("NULL");
} else {
DBDValueHandler valueHandler = DBUtils.findValueHandler(dataSource, attr);
String strValue = valueHandler.getValueDisplayString(attr, item, DBDDisplayFormat.UI);
SQLUtils.appendValue(str, attr, strValue);
}
i++;
if (i >= MAX_ITEMS_IN_STRING) {
break;
}
}
str.append(")");
return str.toString();
}
*/
}