/*
* 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.struct;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.model.DBPDataKind;
import org.jkiss.dbeaver.model.DBPEvaluationContext;
import org.jkiss.dbeaver.model.DBPSaveableObject;
import org.jkiss.dbeaver.model.DBUtils;
import org.jkiss.dbeaver.model.data.DBDAttributeValue;
import org.jkiss.dbeaver.model.data.DBDLabelValuePair;
import org.jkiss.dbeaver.model.data.DBDValueHandler;
import org.jkiss.dbeaver.model.exec.DBCResultSet;
import org.jkiss.dbeaver.model.exec.DBCSession;
import org.jkiss.dbeaver.model.exec.DBCStatement;
import org.jkiss.dbeaver.model.exec.DBCStatementType;
import org.jkiss.dbeaver.model.impl.DBObjectNameCaseTransformer;
import org.jkiss.dbeaver.model.impl.struct.AbstractTableConstraint;
import org.jkiss.dbeaver.model.meta.Property;
import org.jkiss.dbeaver.model.struct.DBSConstraintEnumerable;
import org.jkiss.dbeaver.model.struct.DBSEntityAttribute;
import org.jkiss.dbeaver.model.struct.DBSEntityConstraint;
import org.jkiss.dbeaver.model.struct.DBSEntityConstraintType;
import org.jkiss.dbeaver.model.virtual.DBVEntity;
import org.jkiss.dbeaver.model.virtual.DBVUtils;
import org.jkiss.utils.CommonUtils;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* JDBC abstract constraint
*/
public abstract class JDBCTableConstraint<TABLE extends JDBCTable>
extends AbstractTableConstraint<TABLE>
implements DBSConstraintEnumerable, DBPSaveableObject
{
private boolean persisted;
protected JDBCTableConstraint(TABLE table, String name, @Nullable String description, DBSEntityConstraintType constraintType, boolean persisted) {
super(table, name, description, constraintType);
this.persisted = persisted;
}
// Copy constructor
protected JDBCTableConstraint(TABLE table, DBSEntityConstraint source, boolean persisted) {
super(table, source);
this.persisted = persisted;
}
@NotNull
@Property(viewable = true, editable = true, valueTransformer = DBObjectNameCaseTransformer.class, order = 1)
@Override
public String getName()
{
return super.getName();
}
@Override
public boolean isPersisted()
{
return persisted;
}
@Override
public void setPersisted(boolean persisted)
{
this.persisted = persisted;
}
/**
* Enumerations supported only for unique constraints
* @return true for unique constraint else otherwise
*/
@Override
public boolean supportsEnumeration() {
return getConstraintType().isUnique();
}
/**
* Returns prepared statements for enumeration fetch
* @param session execution context
* @param keyColumn enumeration column.
* @param keyPattern pattern for enumeration values. If null or empty then returns full enumration set
* @param preceedingKeys other constrain key values. May be null.
* @param maxResults maximum enumeration values in result set @return
* @throws DBException
*/
@Override
public Collection<DBDLabelValuePair> getKeyEnumeration(
DBCSession session,
DBSEntityAttribute keyColumn,
Object keyPattern,
List<DBDAttributeValue> preceedingKeys,
int maxResults)
throws DBException
{
if (keyColumn.getParentObject() != this.getTable()) {
throw new IllegalArgumentException("Bad key column argument");
}
// Use default one
return readKeyEnumeration(
session,
keyColumn,
keyPattern,
preceedingKeys,
maxResults);
}
private Collection<DBDLabelValuePair> readKeyEnumeration(
DBCSession session,
DBSEntityAttribute keyColumn,
Object keyPattern,
List<DBDAttributeValue> preceedingKeys,
int maxResults)
throws DBException
{
final TABLE table = getParentObject();
assert table != null;
DBDValueHandler keyValueHandler = DBUtils.findValueHandler(session, keyColumn);
if (keyPattern != null) {
if (keyPattern instanceof CharSequence) {
if (((CharSequence)keyPattern).length() > 0) {
keyPattern = "%" + keyPattern.toString() + "%";
} else {
keyPattern = null;
}
} else if (keyPattern instanceof Number) {
// Subtract gap value to see some values before specified
int gapSize = maxResults / 2;
if (keyPattern instanceof Integer) {
keyPattern = (Integer) keyPattern - gapSize;
} else if (keyPattern instanceof Short) {
keyPattern = (Short) keyPattern - gapSize;
} else if (keyPattern instanceof Long) {
keyPattern = (Long) keyPattern - gapSize;
} else if (keyPattern instanceof Float) {
keyPattern = (Float) keyPattern - gapSize;
} else if (keyPattern instanceof Double) {
keyPattern = (Double) keyPattern - gapSize;
} else if (keyPattern instanceof BigInteger) {
keyPattern = ((BigInteger) keyPattern).subtract(BigInteger.valueOf(gapSize));
} else if (keyPattern instanceof BigDecimal) {
keyPattern = ((BigDecimal) keyPattern).subtract(new BigDecimal(gapSize));
}
} else {
// not supported
keyPattern = null;
}
}
StringBuilder query = new StringBuilder();
query.append("SELECT ").append(DBUtils.getQuotedIdentifier(keyColumn));
String descColumns = DBVUtils.getDictionaryDescriptionColumns(session.getProgressMonitor(), keyColumn);
Collection<DBSEntityAttribute> descAttributes = null;
if (descColumns != null) {
descAttributes = DBVEntity.getDescriptionColumns(session.getProgressMonitor(), table, descColumns);
query.append(", ").append(descColumns);
}
query.append(" FROM ").append(DBUtils.getObjectFullName(table, DBPEvaluationContext.DML));
if (!CommonUtils.isEmpty(preceedingKeys) || keyPattern != null) {
query.append(" WHERE ");
}
boolean hasCond = false;
// Preceeding keys
if (preceedingKeys != null && !preceedingKeys.isEmpty()) {
for (int i = 0; i < preceedingKeys.size(); i++) {
if (hasCond) query.append(" AND ");
query.append(DBUtils.getQuotedIdentifier(getDataSource(), preceedingKeys.get(i).getAttribute().getName())).append(" = ?");
hasCond = true;
}
}
if (keyPattern != null) {
if (hasCond) query.append(" AND (");
query.append(DBUtils.getQuotedIdentifier(keyColumn));
if (keyPattern instanceof CharSequence) {
query.append(" LIKE ?");
} else {
query.append(" >= ?");
}
// Add desc columns conditions
if (keyPattern instanceof CharSequence && descAttributes != null) {
for (DBSEntityAttribute descAttr : descAttributes) {
if (descAttr.getDataKind() == DBPDataKind.STRING) {
query.append(" OR ").append(DBUtils.getQuotedIdentifier(descAttr)).append(" LIKE ?");
}
}
}
if (hasCond) query.append(")");
query.append(" ORDER BY ").append(DBUtils.getQuotedIdentifier(keyColumn));
}
try (DBCStatement dbStat = session.prepareStatement(DBCStatementType.QUERY, query.toString(), false, false, false)) {
int paramPos = 0;
if (preceedingKeys != null && !preceedingKeys.isEmpty()) {
for (DBDAttributeValue precAttribute : preceedingKeys) {
DBDValueHandler precValueHandler = DBUtils.findValueHandler(session, precAttribute.getAttribute());
precValueHandler.bindValueObject(session, dbStat, precAttribute.getAttribute(), paramPos++, precAttribute.getValue());
}
}
if (keyPattern != null) {
keyValueHandler.bindValueObject(session, dbStat, keyColumn, paramPos++, keyPattern);
}
if (keyPattern instanceof CharSequence && descAttributes != null) {
for (DBSEntityAttribute descAttr : descAttributes) {
if (descAttr.getDataKind() == DBPDataKind.STRING) {
final DBDValueHandler valueHandler = DBUtils.findValueHandler(session, descAttr);
valueHandler.bindValueObject(session, dbStat, keyColumn, paramPos++, keyPattern);
}
}
}
dbStat.setLimit(0, maxResults);
if (dbStat.executeStatement()) {
try (DBCResultSet dbResult = dbStat.openResultSet()) {
return DBVUtils.readDictionaryRows(session, keyColumn, keyValueHandler, dbResult);
}
} else {
return Collections.emptyList();
}
}
}
}