/*
* 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;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IAdaptable;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.DBException;
import org.jkiss.dbeaver.Log;
import org.jkiss.dbeaver.ModelPreferences;
import org.jkiss.dbeaver.model.app.DBPDataSourceRegistry;
import org.jkiss.dbeaver.model.data.*;
import org.jkiss.dbeaver.model.exec.*;
import org.jkiss.dbeaver.model.impl.data.DefaultValueHandler;
import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
import org.jkiss.dbeaver.model.sql.*;
import org.jkiss.dbeaver.model.struct.*;
import org.jkiss.dbeaver.model.struct.rdb.*;
import org.jkiss.dbeaver.utils.GeneralUtils;
import org.jkiss.utils.CommonUtils;
import java.util.*;
/**
* DBUtils
*/
public final class DBUtils {
private static final Log log = Log.getLog(DBUtils.class);
@NotNull
public static String getQuotedIdentifier(@NotNull DBSObject object)
{
return getQuotedIdentifier(object.getDataSource(), object.getName());
}
public static boolean isQuotedIdentifier(@NotNull DBPDataSource dataSource, @NotNull String str)
{
if (dataSource instanceof SQLDataSource) {
final String quote = ((SQLDataSource) dataSource).getSQLDialect().getIdentifierQuoteString();
return quote != null && str.startsWith(quote) && str.endsWith(quote);
} else {
return false;
}
}
@NotNull
public static String getUnQuotedIdentifier(@NotNull DBPDataSource dataSource, @NotNull String str)
{
if (dataSource instanceof SQLDataSource) {
String quote = ((SQLDataSource) dataSource).getSQLDialect().getIdentifierQuoteString();
if (quote == null) {
quote = SQLConstants.DEFAULT_IDENTIFIER_QUOTE;
}
if (str.startsWith(quote) && str.endsWith(quote)) {
return str.substring(quote.length(), str.length() - quote.length());
}
}
return str;
}
@NotNull
public static String getUnQuotedIdentifier(@NotNull String str, String quote)
{
if (quote != null && str.startsWith(quote) && str.endsWith(quote)) {
return str.substring(quote.length(), str.length() - quote.length());
}
return str;
}
@NotNull
public static String getQuotedIdentifier(@NotNull DBPDataSource dataSource, @NotNull String str)
{
if (dataSource instanceof SQLDataSource) {
return getQuotedIdentifier((SQLDataSource)dataSource, str, true);
} else {
return str;
}
}
@NotNull
public static String getQuotedIdentifier(@NotNull SQLDataSource dataSource, @NotNull String str, boolean caseSensitiveNames)
{
final SQLDialect sqlDialect = dataSource.getSQLDialect();
String quoteString = sqlDialect.getIdentifierQuoteString();
if (quoteString == null) {
return str;
}
if (str.startsWith(quoteString) && str.endsWith(quoteString)) {
// Already quoted
return str;
}
// Check for keyword conflict
final DBPKeywordType keywordType = sqlDialect.getKeywordType(str);
boolean hasBadChars =
(keywordType == DBPKeywordType.KEYWORD || keywordType == DBPKeywordType.TYPE) &&
sqlDialect.isQuoteReservedWords();
if (!hasBadChars && !str.isEmpty()) {
hasBadChars = Character.isDigit(str.charAt(0));
}
if (caseSensitiveNames) {
// Check for case of quoted idents. Do not check for unquoted case - we don't need to quote em anyway
if (!hasBadChars && sqlDialect.supportsQuotedMixedCase()) {
// See how unquoted idents are stored
// If passed identifier case differs from unquoted then we need to escape it
if (sqlDialect.storesUnquotedCase() == DBPIdentifierCase.UPPER) {
hasBadChars = !str.equals(str.toUpperCase());
} else if (sqlDialect.storesUnquotedCase() == DBPIdentifierCase.LOWER) {
hasBadChars = !str.equals(str.toLowerCase());
}
}
}
// Check for bad characters
if (!hasBadChars && !str.isEmpty()) {
if (str.charAt(0) == '_') {
hasBadChars = true;
} else {
for (int i = 0; i < str.length(); i++) {
if (!sqlDialect.validUnquotedCharacter(str.charAt(i))) {
hasBadChars = true;
break;
}
}
}
}
if (!hasBadChars) {
return str;
}
// Escape quote chars
if (str.contains(quoteString)) {
str = str.replace(quoteString, quoteString + quoteString);
}
return quoteString + str + quoteString;
}
@NotNull
public static String getFullQualifiedName(@NotNull DBPDataSource dataSource, @NotNull DBPNamedObject ... path)
{
StringBuilder name = new StringBuilder(20 * path.length);
if (!(dataSource instanceof SQLDataSource)) {
// It is not SQL identifier, let's just make it simple then
for (DBPNamedObject namePart : path) {
if (name.length() > 0) { name.append('.'); }
name.append(namePart.getName());
}
} else {
final SQLDialect sqlDialect = ((SQLDataSource) dataSource).getSQLDialect();
DBPNamedObject parent = null;
for (DBPNamedObject namePart : path) {
if (namePart == null) {
continue;
}
if (namePart instanceof DBSCatalog && ((sqlDialect.getCatalogUsage() & SQLDialect.USAGE_DML) == 0)) {
// Do not use catalog name in FQ name
continue;
}
if (namePart instanceof DBSSchema && ((sqlDialect.getSchemaUsage() & SQLDialect.USAGE_DML) == 0)) {
// Do not use schema name in FQ name
continue;
}
// Check for valid object name
if (!isValidObjectName(namePart.getName())) {
continue;
}
if (name.length() > 0) {
if (parent instanceof DBSCatalog) {
if (!sqlDialect.isCatalogAtStart()) {
log.warn("Catalog name should be at the start of full-qualified name!");
}
name.append(sqlDialect.getCatalogSeparator());
} else {
name.append(sqlDialect.getStructSeparator());
}
}
name.append(DBUtils.getQuotedIdentifier(dataSource, namePart.getName()));
parent = namePart;
}
}
return name.toString();
}
@NotNull
public static String getSimpleQualifiedName(@NotNull Object... names)
{
StringBuilder name = new StringBuilder(names.length * 16);
for (Object namePart : names) {
if (namePart == null) {
continue;
}
if (name.length() > 0) {
name.append('.');
}
name.append(namePart);
}
return name.toString();
}
/**
* Checks that object has valid object name.
* Some DB objects have dummy names (like "" or ".") - we won't use them for certain purposes.
* @param name object name
* @return true or false
*/
public static boolean isValidObjectName(@Nullable String name)
{
if (name == null || name.isEmpty()) {
return false;
}
boolean validName = false;
for (int i = 0; i < name.length(); i++) {
if (Character.isLetterOrDigit(name.charAt(i))) {
validName = true;
break;
}
}
return validName;
}
/**
* Finds catalog, schema or table within specified object container
* @param monitor progress monitor
* @param rootSC container
* @param catalogName catalog name (optional)
* @param schemaName schema name (optional)
* @param objectName table name (optional)
* @return found object or null
* @throws DBException
*/
@Nullable
public static DBSObject getObjectByPath(
@NotNull DBRProgressMonitor monitor,
@NotNull DBSObjectContainer rootSC,
@Nullable String catalogName,
@Nullable String schemaName,
@Nullable String objectName)
throws DBException
{
if (!CommonUtils.isEmpty(catalogName) && !CommonUtils.isEmpty(schemaName)) {
// We have both both - just search both
DBSObject catalog = rootSC.getChild(monitor, catalogName);
if (!(catalog instanceof DBSObjectContainer)) {
return null;
}
rootSC = (DBSObjectContainer) catalog;
DBSObject schema = rootSC.getChild(monitor, schemaName);
if (!(schema instanceof DBSObjectContainer)) {
return null;
}
rootSC = (DBSObjectContainer) schema;
} else if (!CommonUtils.isEmpty(catalogName) || !CommonUtils.isEmpty(schemaName)) {
// One container name
String containerName = !CommonUtils.isEmpty(catalogName) ? catalogName : schemaName;
DBSObject sc = rootSC.getChild(monitor, containerName);
if (!(sc instanceof DBSObjectContainer)) {
// Not found - try to find in selected object
DBSObject selectedObject = getSelectedObject(rootSC, false);
if (selectedObject instanceof DBSObjectContainer) {
sc = ((DBSObjectContainer) selectedObject).getChild(monitor, containerName);
}
if (!(sc instanceof DBSObjectContainer)) {
return null;
}
}
rootSC = (DBSObjectContainer) sc;
}
if (objectName == null) {
return rootSC;
}
final DBSObject object = rootSC.getChild(monitor, objectName);
if (object instanceof DBSEntity) {
return object;
} else {
// Child is not an entity. May be catalog/schema names was omitted.
// Try to use selected object
DBSObject selectedObject = DBUtils.getSelectedObject(rootSC, true);
if (selectedObject instanceof DBSObjectContainer) {
return ((DBSObjectContainer) selectedObject).getChild(monitor, objectName);
}
// Table container not found
return object;
}
}
@Nullable
public static DBSObject findNestedObject(
@NotNull DBRProgressMonitor monitor,
@NotNull DBSObjectContainer parent,
@NotNull List<String> names)
throws DBException
{
for (int i = 0; i < names.size(); i++) {
String childName = names.get(i);
DBSObject child = parent.getChild(monitor, childName);
if (child == null) {
DBSObjectSelector selector = DBUtils.getAdapter(DBSObjectSelector.class, parent);
if (selector != null) {
DBSObjectContainer container = DBUtils.getAdapter(DBSObjectContainer.class, selector.getDefaultObject());
if (container != null) {
parent = container;
child = parent.getChild(monitor, childName);
}
}
}
if (child == null) {
break;
}
if (i == names.size() - 1) {
return child;
}
if (child instanceof DBSObjectContainer) {
parent = DBSObjectContainer.class.cast(child);
} else {
break;
}
}
return null;
}
/**
* Finds object by its name (case insensitive)
*
* @param theList object list
* @param objectName object name
* @return object or null
*/
@Nullable
public static <T extends DBPNamedObject> T findObject(@Nullable Collection<T> theList, String objectName)
{
if (theList != null && !theList.isEmpty()) {
for (T object : theList) {
if (object.getName().equalsIgnoreCase(objectName)) {
return object;
}
}
}
return null;
}
@Nullable
public static <T extends DBPNamedObject> T findObject(@Nullable List<T> theList, String objectName)
{
if (theList != null) {
int size = theList.size();
for (int i = 0; i < size; i++) {
if (theList.get(i).getName().equalsIgnoreCase(objectName)) {
return theList.get(i);
}
}
}
return null;
}
/**
* Finds object by its name (case insensitive)
*
* @param theList object list
* @param objectName object name
* @return object or null
*/
@Nullable
public static <T extends DBPNamedObject> List<T> findObjects(@Nullable Collection<T> theList, @Nullable String objectName)
{
if (theList != null && !theList.isEmpty()) {
List<T> result = new ArrayList<>();
for (T object : theList) {
if (object.getName().equalsIgnoreCase(objectName)) {
result.add(object);
}
}
return result;
}
return null;
}
@Nullable
public static <T> T getAdapter(@NotNull Class<T> adapterType, @Nullable Object object)
{
if (object instanceof DBPDataSourceContainer) {
// Root object's parent is data source container (not datasource)
// So try to get adapter from real datasource object
object = ((DBPDataSourceContainer)object).getDataSource();
}
if (object == null) {
return null;
}
if (adapterType.isAssignableFrom(object.getClass())) {
return adapterType.cast(object);
} else if (object instanceof IAdaptable) {
return adapterType.cast(((IAdaptable)object).getAdapter(adapterType));
} else {
return null;
}
}
@Nullable
public static <T> T getParentAdapter(@NotNull Class<T> i, DBSObject object)
{
if (object == null) {
return null;
}
DBSObject parent = object.getParentObject();
if (parent == null) {
return null;
}
T adapter = getAdapter(i, parent);
// In some cases parent's adapter is object itself (e.g. DS maybe DS adapter of container)
return adapter == object ? null : adapter;
}
/**
* Search for virtual entity descriptor
* @param object object
* @return object path
*/
@NotNull
public static DBSObject[] getObjectPath(@NotNull DBSObject object, boolean includeSelf)
{
int depth = 0;
final DBSObject root = includeSelf ? object : object.getParentObject();
for (DBSObject obj = root; obj != null; obj = obj.getParentObject()) {
depth++;
}
DBSObject[] path = new DBSObject[depth];
for (DBSObject obj = root; obj != null; obj = obj.getParentObject()) {
path[depth-- - 1] = obj;
}
return path;
}
public static boolean isNullValue(@Nullable Object value)
{
return (value == null || (value instanceof DBDValue && ((DBDValue) value).isNull()));
}
@Nullable
public static Object makeNullValue(@NotNull DBCSession session, @NotNull DBDValueHandler valueHandler, @NotNull DBSTypedObject type) throws DBCException
{
return valueHandler.getValueFromObject(session, type, null, false);
}
@NotNull
public static DBDAttributeBindingMeta getAttributeBinding(@NotNull DBCSession session, @NotNull DBCAttributeMetaData attributeMeta)
{
return new DBDAttributeBindingMeta(session, attributeMeta);
}
@NotNull
public static DBDValueHandler findValueHandler(@NotNull DBCSession session, @NotNull DBSTypedObject column)
{
return findValueHandler(session.getDataSource(), session, column);
}
@NotNull
public static DBDValueHandler findValueHandler(@NotNull DBPDataSource dataSource, @NotNull DBSTypedObject column)
{
return findValueHandler(dataSource, dataSource.getContainer(), column);
}
@NotNull
public static DBDValueHandler findValueHandler(@NotNull DBPDataSource dataSource, @Nullable DBDPreferences preferences, @NotNull DBSTypedObject column)
{
DBDValueHandler typeHandler = null;
// Get handler provider from datasource
DBDValueHandlerProvider typeProvider = getAdapter(DBDValueHandlerProvider.class, dataSource);
if (typeProvider != null) {
typeHandler = typeProvider.getValueHandler(dataSource, preferences, column);
if (typeHandler != null) {
return typeHandler;
}
}
// Get handler provider from registry
typeProvider = dataSource.getContainer().getPlatform().getValueHandlerRegistry().getDataTypeProvider(
dataSource, column);
if (typeProvider != null) {
typeHandler = typeProvider.getValueHandler(dataSource, preferences, column);
}
// Use default handler
if (typeHandler == null) {
if (preferences == null) {
typeHandler = DefaultValueHandler.INSTANCE;
} else {
typeHandler = preferences.getDefaultValueHandler();
}
}
return typeHandler;
}
/**
* Identifying association is an association which associated entity's attributes are included into owner entity primary key. I.e. they
* identifies entity.
*/
public static boolean isIdentifyingAssociation(@NotNull DBRProgressMonitor monitor, @NotNull DBSEntityAssociation association) throws DBException
{
if (!(association instanceof DBSEntityReferrer)) {
return false;
}
final DBSEntityReferrer referrer = (DBSEntityReferrer)association;
final DBSEntity refEntity = association.getAssociatedEntity();
final DBSEntity ownerEntity = association.getParentObject();
assert ownerEntity != null;
if (refEntity == ownerEntity) {
// Can't migrate into itself
return false;
}
// Migrating association is: if all referenced attributes are included in some unique key
List<DBSEntityAttribute> ownAttrs = getEntityAttributes(monitor, referrer);
Collection<? extends DBSEntityConstraint> constraints = ownerEntity.getConstraints(monitor);
if (constraints != null) {
boolean hasPrimaryKey = false;
for (DBSEntityConstraint constraint : constraints) {
if (constraint.getConstraintType() == DBSEntityConstraintType.PRIMARY_KEY) {
hasPrimaryKey = true;
break;
}
}
for (DBSEntityConstraint constraint : constraints) {
if (constraint instanceof DBSEntityReferrer &&
((hasPrimaryKey && constraint.getConstraintType() == DBSEntityConstraintType.PRIMARY_KEY) ||
(!hasPrimaryKey && constraint.getConstraintType().isUnique())))
{
List<DBSEntityAttribute> constAttrs = getEntityAttributes(monitor, (DBSEntityReferrer) constraint);
boolean included = true;
for (DBSEntityAttribute attr : ownAttrs) {
if (!constAttrs.contains(attr)) {
included = false;
break;
}
}
if (included) {
return true;
}
}
}
}
return false;
}
@NotNull
public static String getDefaultDataTypeName(@NotNull DBPDataSource dataSource, DBPDataKind dataKind)
{
if (dataSource instanceof DBPDataTypeProvider) {
return ((DBPDataTypeProvider) dataSource).getDefaultDataTypeName(dataKind);
} else {
// Unsupported data kind
return "?";
}
}
@Nullable
public static DBDAttributeBinding findBinding(@NotNull Collection<DBDAttributeBinding> bindings, @NotNull DBSAttributeBase attribute)
{
for (DBDAttributeBinding binding : bindings) {
if (binding.matches(attribute, true)) {
return binding;
}
List<DBDAttributeBinding> nestedBindings = binding.getNestedBindings();
if (nestedBindings != null) {
DBDAttributeBinding subBinding = findBinding(nestedBindings, attribute);
if (subBinding != null) {
return subBinding;
}
}
}
return null;
}
@Nullable
public static DBDAttributeBinding findBinding(@NotNull DBDAttributeBinding[] bindings, @Nullable DBSAttributeBase attribute)
{
if (attribute == null) {
return null;
}
for (DBDAttributeBinding binding : bindings) {
if (binding.matches(attribute, true)) {
return binding;
}
List<DBDAttributeBinding> nestedBindings = binding.getNestedBindings();
if (nestedBindings != null) {
DBDAttributeBinding subBinding = findBinding(nestedBindings, attribute);
if (subBinding != null) {
return subBinding;
}
}
}
return null;
}
@NotNull
public static List<DBSEntityReferrer> getAttributeReferrers(@NotNull DBRProgressMonitor monitor, @NotNull DBSEntityAttribute entityAttribute)
throws DBException
{
DBSEntity entity = entityAttribute.getParentObject();
assert entity != null;
List<DBSEntityReferrer> refs = null;
Collection<? extends DBSEntityAssociation> associations = entity.getAssociations(monitor);
if (associations != null) {
for (DBSEntityAssociation fk : associations) {
if (fk instanceof DBSEntityReferrer && DBUtils.getConstraintAttribute(monitor, (DBSEntityReferrer) fk, entityAttribute) != null) {
if (refs == null) {
refs = new ArrayList<>();
}
refs.add((DBSEntityReferrer)fk);
}
}
}
return refs != null ? refs : Collections.<DBSEntityReferrer>emptyList();
}
@NotNull
public static Collection<? extends DBSEntityAttribute> getBestTableIdentifier(@NotNull DBRProgressMonitor monitor, @NotNull DBSEntity entity)
throws DBException
{
if (entity instanceof DBSTable && ((DBSTable) entity).isView()) {
return Collections.emptyList();
}
if (CommonUtils.isEmpty(entity.getAttributes(monitor))) {
return Collections.emptyList();
}
List<DBSEntityConstraint> identifiers = new ArrayList<>();
// Check indexes
if (entity instanceof DBSTable) {
try {
Collection<? extends DBSTableIndex> indexes = ((DBSTable)entity).getIndexes(monitor);
if (!CommonUtils.isEmpty(indexes)) {
for (DBSTableIndex index : indexes) {
if (isIdentifierIndex(monitor, index)) {
identifiers.add(index);
}
}
}
} catch (DBException e) {
log.debug(e);
}
}
// Check constraints only if no unique indexes found
if (identifiers.isEmpty()) {
Collection<? extends DBSEntityConstraint> uniqueKeys = entity.getConstraints(monitor);
if (uniqueKeys != null) {
for (DBSEntityConstraint constraint : uniqueKeys) {
if (isIdentifierConstraint(monitor, constraint)) {
identifiers.add(constraint);
}
}
}
}
if (!identifiers.isEmpty()) {
// Find PK or unique key
DBSEntityConstraint uniqueId = null;
//DBSEntityConstraint uniqueIndex = null;
for (DBSEntityConstraint id : identifiers) {
if (id instanceof DBSEntityReferrer && id.getConstraintType() == DBSEntityConstraintType.PRIMARY_KEY) {
return getEntityAttributes(monitor, (DBSEntityReferrer) id);
} else if (id.getConstraintType().isUnique()) {
uniqueId = id;
} else if (id instanceof DBSTableIndex && ((DBSTableIndex) id).isUnique()) {
uniqueId = id;
}
}
return uniqueId instanceof DBSEntityReferrer ?
getEntityAttributes(monitor, (DBSEntityReferrer)uniqueId)
: Collections.<DBSTableColumn>emptyList();
} else {
return Collections.emptyList();
}
}
public static boolean isIdentifierIndex(DBRProgressMonitor monitor, DBSTableIndex index) throws DBException {
if (!index.isUnique()) {
return false;
}
List<? extends DBSTableIndexColumn> attrs = index.getAttributeReferences(monitor);
if (attrs == null || attrs.isEmpty()) {
return false;
}
for (DBSTableIndexColumn col : attrs) {
if (col.getTableColumn() == null || !col.getTableColumn().isRequired()) {
// Do not use indexes with NULL columns (because they are not actually unique: #424)
return false;
}
}
return true;
}
public static boolean isIdentifierConstraint(DBRProgressMonitor monitor, DBSEntityConstraint constraint) throws DBException {
if (constraint instanceof DBSEntityReferrer && constraint.getConstraintType().isUnique()) {
List<? extends DBSEntityAttributeRef> attrs = ((DBSEntityReferrer) constraint).getAttributeReferences(monitor);
if (attrs == null || attrs.isEmpty()) {
return false;
}
for (DBSEntityAttributeRef col : attrs) {
if (col.getAttribute() == null || !col.getAttribute().isRequired()) {
// Do not use constraints with NULL columns (because they are not actually unique: #424)
return false;
}
}
return true;
}
return false;
}
public static DBSEntityConstraint findEntityConstraint(@NotNull DBRProgressMonitor monitor, @NotNull DBSEntity entity, @NotNull Collection<? extends DBSEntityAttribute> attributes)
throws DBException
{
// Check constraints
Collection<? extends DBSEntityConstraint> constraints = entity.getConstraints(monitor);
if (!CommonUtils.isEmpty(constraints)) {
for (DBSEntityConstraint constraint : constraints) {
if (constraint instanceof DBSEntityReferrer && referrerMatches(monitor, (DBSEntityReferrer)constraint, attributes)) {
return constraint;
}
}
}
if (entity instanceof DBSTable) {
Collection<? extends DBSTableIndex> indexes = ((DBSTable) entity).getIndexes(monitor);
if (!CommonUtils.isEmpty(indexes)) {
for (DBSTableIndex index : indexes) {
if (index.isUnique() && referrerMatches(monitor, index, attributes)) {
return index;
}
}
}
}
return null;
}
public static boolean referrerMatches(@NotNull DBRProgressMonitor monitor, @NotNull DBSEntityReferrer referrer, @NotNull Collection<? extends DBSEntityAttribute> attributes) throws DBException {
final List<? extends DBSEntityAttributeRef> refs = referrer.getAttributeReferences(monitor);
if (refs != null && !refs.isEmpty()) {
Iterator<? extends DBSEntityAttribute> attrIterator = attributes.iterator();
for (DBSEntityAttributeRef ref : refs) {
if (!attrIterator.hasNext()) {
return false;
}
if (ref.getAttribute() == null ||
!CommonUtils.equalObjects(ref.getAttribute().getName(), attrIterator.next().getName()))
{
return false;
}
}
return true;
}
return false;
}
@NotNull
public static List<DBSEntityAttribute> getEntityAttributes(@NotNull DBRProgressMonitor monitor, @Nullable DBSEntityReferrer referrer)
{
Collection<? extends DBSEntityAttributeRef> constraintColumns = null;
if (referrer != null) {
try {
constraintColumns = referrer.getAttributeReferences(monitor);
} catch (DBException e) {
log.warn("Error reading reference attributes", e);
}
}
if (constraintColumns == null) {
return Collections.emptyList();
}
List<DBSEntityAttribute> attributes = new ArrayList<>(constraintColumns.size());
for (DBSEntityAttributeRef column : constraintColumns) {
final DBSEntityAttribute attribute = column.getAttribute();
if (attribute != null) {
attributes.add(attribute);
}
}
return attributes;
}
@Nullable
public static DBSEntityAttributeRef getConstraintAttribute(@NotNull DBRProgressMonitor monitor, @NotNull DBSEntityReferrer constraint, @NotNull DBSEntityAttribute tableColumn) throws DBException
{
Collection<? extends DBSEntityAttributeRef> columns = constraint.getAttributeReferences(monitor);
if (columns != null) {
for (DBSEntityAttributeRef constraintColumn : columns) {
if (constraintColumn.getAttribute() == tableColumn) {
return constraintColumn;
}
}
}
return null;
}
@Nullable
public static DBSEntityAttributeRef getConstraintAttribute(@NotNull DBRProgressMonitor monitor, @NotNull DBSEntityReferrer constraint, @NotNull String columnName) throws DBException
{
Collection<? extends DBSEntityAttributeRef> columns = constraint.getAttributeReferences(monitor);
if (columns != null) {
for (DBSEntityAttributeRef constraintColumn : columns) {
final DBSEntityAttribute attribute = constraintColumn.getAttribute();
if (attribute != null && attribute.getName().equals(columnName)) {
return constraintColumn;
}
}
}
return null;
}
/**
* Return reference column of referrer association (FK).
* Assumes that columns in both constraints are in the same order.
*/
@Nullable
public static DBSEntityAttribute getReferenceAttribute(@NotNull DBRProgressMonitor monitor, @NotNull DBSEntityAssociation association, @NotNull DBSEntityAttribute tableColumn) throws DBException
{
final DBSEntityConstraint refConstr = association.getReferencedConstraint();
if (association instanceof DBSEntityReferrer && refConstr instanceof DBSEntityReferrer) {
final Collection<? extends DBSEntityAttributeRef> ownAttrs = ((DBSEntityReferrer) association).getAttributeReferences(monitor);
final Collection<? extends DBSEntityAttributeRef> refAttrs = ((DBSEntityReferrer) refConstr).getAttributeReferences(monitor);
if (ownAttrs == null || refAttrs == null || ownAttrs.size() != refAttrs.size()) {
log.error("Invalid internal state of referrer association");
return null;
}
final Iterator<? extends DBSEntityAttributeRef> ownIterator = ownAttrs.iterator();
final Iterator<? extends DBSEntityAttributeRef> refIterator = refAttrs.iterator();
while (ownIterator.hasNext()) {
if (ownIterator.next().getAttribute() == tableColumn) {
return refIterator.next().getAttribute();
}
refIterator.next();
}
}
return null;
}
@NotNull
public static DBCStatement makeStatement(
@NotNull DBCExecutionSource executionSource,
@NotNull DBCSession session,
@NotNull DBCStatementType statementType,
@NotNull String query,
long offset,
long maxRows) throws DBCException
{
SQLQuery sqlQuery = new SQLQuery(session.getDataSource(), query);
return makeStatement(
executionSource,
session,
statementType,
sqlQuery,
offset,
maxRows);
}
@NotNull
public static DBCStatement makeStatement(
@NotNull DBCExecutionSource executionSource,
@NotNull DBCSession session,
@NotNull DBCStatementType statementType,
@NotNull SQLQuery sqlQuery,
long offset,
long maxRows)
throws DBCException
{
// We need to detect whether it is a plain select statement
// or some DML. For DML statements we mustn't set limits
// because it sets update rows limit [SQL Server]
boolean selectQuery = sqlQuery.getType() == SQLQueryType.SELECT && sqlQuery.isPlainSelect();
final boolean hasLimits = selectQuery && offset >= 0 && maxRows > 0;
DBCQueryTransformer limitTransformer = null, fetchAllTransformer = null;
if (selectQuery) {
DBCQueryTransformProvider transformProvider = DBUtils.getAdapter(DBCQueryTransformProvider.class, session.getDataSource());
if (transformProvider != null) {
if (hasLimits) {
if (session.getDataSource().getContainer().getPreferenceStore().getBoolean(ModelPreferences.RESULT_SET_MAX_ROWS_USE_SQL)) {
limitTransformer = transformProvider.createQueryTransformer(DBCQueryTransformType.RESULT_SET_LIMIT);
}
} else if (offset <= 0 && maxRows <= 0) {
fetchAllTransformer = transformProvider.createQueryTransformer(DBCQueryTransformType.FETCH_ALL_TABLE);
}
}
}
String queryText;
if (hasLimits && limitTransformer != null) {
limitTransformer.setParameters(offset, maxRows);
queryText = limitTransformer.transformQueryString(sqlQuery);
} else if (fetchAllTransformer != null) {
queryText = fetchAllTransformer.transformQueryString(sqlQuery);
} else {
queryText = sqlQuery.getText();
}
DBCStatement dbStat = statementType == DBCStatementType.SCRIPT ?
createStatement(session, queryText, hasLimits) :
makeStatement(session, queryText, hasLimits);
dbStat.setStatementSource(executionSource);
if (hasLimits || offset > 0) {
if (limitTransformer == null) {
// Set explicit limit - it is safe because we pretty sure that this is a plain SELECT query
dbStat.setLimit(offset, maxRows);
} else {
limitTransformer.transformStatement(dbStat, 0);
}
} else if (fetchAllTransformer != null) {
fetchAllTransformer.transformStatement(dbStat, 0);
}
return dbStat;
}
@NotNull
public static DBCStatement createStatement(
@NotNull DBCSession session,
@NotNull String query,
boolean scrollable) throws DBCException
{
DBCStatementType statementType = DBCStatementType.SCRIPT;
query = SQLUtils.makeUnifiedLineFeeds(query);
if (SQLUtils.isExecQuery(SQLUtils.getDialectFromObject(session.getDataSource()), query)) {
statementType = DBCStatementType.EXEC;
}
return session.prepareStatement(
statementType,
query,
scrollable && session.getDataSource().getInfo().supportsResultSetScroll(),
false,
false);
}
@NotNull
public static DBCStatement makeStatement(
@NotNull DBCSession session,
@NotNull String query,
boolean scrollable) throws DBCException
{
DBCStatementType statementType = DBCStatementType.QUERY;
// Normalize query
query = SQLUtils.makeUnifiedLineFeeds(query);
if (SQLUtils.isExecQuery(SQLUtils.getDialectFromObject(session.getDataSource()), query)) {
statementType = DBCStatementType.EXEC;
}
return session.prepareStatement(
statementType,
query,
scrollable && session.getDataSource().getInfo().supportsResultSetScroll(),
false,
false);
}
public static void fireObjectUpdate(@NotNull DBSObject object)
{
fireObjectUpdate(object, null);
}
public static void fireObjectUpdate(DBSObject object, @Nullable Object data)
{
final DBPDataSourceContainer container = getContainer(object);
if (container != null) {
container.fireEvent(new DBPEvent(DBPEvent.Action.OBJECT_UPDATE, object, data));
}
}
public static void fireObjectUpdate(DBSObject object, boolean enabled)
{
final DBPDataSourceContainer container = getContainer(object);
if (container != null) {
container.fireEvent(new DBPEvent(DBPEvent.Action.OBJECT_UPDATE, object, enabled));
}
}
public static void fireObjectAdd(DBSObject object)
{
final DBPDataSourceContainer container = getContainer(object);
if (container != null) {
container.fireEvent(new DBPEvent(DBPEvent.Action.OBJECT_ADD, object));
}
}
public static void fireObjectRemove(DBSObject object)
{
final DBPDataSourceContainer container = getContainer(object);
if (container != null) {
container.fireEvent(new DBPEvent(DBPEvent.Action.OBJECT_REMOVE, object));
}
}
public static void fireObjectSelect(DBSObject object, boolean select)
{
final DBPDataSourceContainer container = getContainer(object);
if (container != null) {
container.fireEvent(new DBPEvent(DBPEvent.Action.OBJECT_SELECT, object, select));
}
}
/**
* Refresh object in UI
*/
public static void fireObjectRefresh(DBSObject object)
{
// Select with true parameter is the same as refresh
fireObjectSelect(object, true);
}
@NotNull
public static String getObjectUniqueName(@NotNull DBSObject object)
{
if (object instanceof DBPUniqueObject) {
return ((DBPUniqueObject) object).getUniqueName();
} else {
return object.getName();
}
}
@Nullable
public static DBSDataType findBestDataType(@NotNull Collection<? extends DBSDataType> allTypes, @NotNull String ... typeNames)
{
for (String testType : typeNames) {
for (DBSDataType dataType : allTypes) {
if (dataType.getName().equalsIgnoreCase(testType)) {
return dataType;
}
}
}
return null;
}
@Nullable
public static DBSDataType resolveDataType(
@NotNull DBRProgressMonitor monitor,
@NotNull DBPDataSource dataSource,
@NotNull String fullTypeName)
throws DBException
{
DBPDataTypeProvider dataTypeProvider = getAdapter(DBPDataTypeProvider.class, dataSource);
if (dataTypeProvider == null) {
// NoSuchElementException data type provider
return null;
}
return dataTypeProvider.resolveDataType(monitor, fullTypeName);
}
@Nullable
public static DBSDataType getLocalDataType(
@NotNull DBPDataSource dataSource,
@NotNull String fullTypeName)
{
DBPDataTypeProvider dataTypeProvider = getAdapter(DBPDataTypeProvider.class, dataSource);
if (dataTypeProvider == null) {
return null;
}
return dataTypeProvider.getLocalDataType(fullTypeName);
}
public static DBPObject getPublicObject(@NotNull DBPObject object)
{
if (object instanceof DBPDataSourceContainer) {
return ((DBPDataSourceContainer) object).getDataSource();
} else {
return object;
}
}
@Nullable
public static DBPDataSourceContainer getContainer(@Nullable DBSObject object)
{
if (object == null) {
return null;
}
if (object instanceof DBPDataSourceContainer) {
return (DBPDataSourceContainer) object;
}
final DBPDataSource dataSource = object.getDataSource();
return dataSource == null ? null : dataSource.getContainer();
}
@NotNull
public static DBPDataSourceRegistry getObjectRegistry(@NotNull DBSObject object)
{
DBPDataSourceContainer container;
if (object instanceof DBPDataSourceContainer) {
container = (DBPDataSourceContainer) object;
} else {
DBPDataSource dataSource = object.getDataSource();
container = dataSource.getContainer();
}
return container.getRegistry();
}
@NotNull
public static IProject getObjectOwnerProject(DBSObject object) {
return getObjectRegistry(object).getProject();
}
@NotNull
public static String getObjectShortName(Object object)
{
String strValue;
if (object instanceof DBPNamedObject) {
strValue = ((DBPNamedObject)object).getName();
} else {
strValue = String.valueOf(object);
}
return strValue;
}
@NotNull
public static String getObjectFullName(@NotNull DBPNamedObject object, DBPEvaluationContext context)
{
if (object instanceof DBPQualifiedObject) {
return ((DBPQualifiedObject) object).getFullyQualifiedName(context);
} else if (object instanceof DBSObject) {
return getObjectFullName(((DBSObject) object).getDataSource(), object, context);
} else {
return object.getName();
}
}
@NotNull
public static String getObjectFullName(@NotNull DBPDataSource dataSource, @NotNull DBPNamedObject object, DBPEvaluationContext context)
{
if (object instanceof DBPQualifiedObject) {
return ((DBPQualifiedObject) object).getFullyQualifiedName(context);
} else {
return getQuotedIdentifier(dataSource, object.getName());
}
}
@NotNull
public static String getFullTypeName(@NotNull DBSTypedObject typedObject)
{
String typeName = typedObject.getTypeName();
String typeModifiers = SQLUtils.getColumnTypeModifiers(typedObject, typeName, typedObject.getDataKind());
return typeModifiers == null ? typeName : (typeName + CommonUtils.notEmpty(typeModifiers));
}
public static void releaseValue(@Nullable Object value)
{
if (value instanceof DBDValue) {
((DBDValue)value).release();
}
}
public static void resetValue(@Nullable Object value)
{
if (value instanceof DBDContent) {
((DBDContent)value).resetContents();
}
}
@NotNull
public static DBCLogicalOperator[] getAttributeOperators(DBSTypedObject attribute) {
if (attribute instanceof DBSTypedObjectEx) {
DBSDataType dataType = ((DBSTypedObjectEx) attribute).getDataType();
if (dataType != null) {
return dataType.getSupportedOperators(attribute);
}
}
return getDefaultOperators(attribute);
}
@NotNull
public static DBCLogicalOperator[] getDefaultOperators(DBSTypedObject attribute) {
List<DBCLogicalOperator> operators = new ArrayList<>();
DBPDataKind dataKind = attribute.getDataKind();
if (attribute instanceof DBSAttributeBase && !((DBSAttributeBase)attribute).isRequired()) {
operators.add(DBCLogicalOperator.IS_NULL);
operators.add(DBCLogicalOperator.IS_NOT_NULL);
}
if (dataKind == DBPDataKind.BOOLEAN || dataKind == DBPDataKind.ROWID || dataKind == DBPDataKind.OBJECT || dataKind == DBPDataKind.BINARY) {
operators.add(DBCLogicalOperator.EQUALS);
operators.add(DBCLogicalOperator.NOT_EQUALS);
} else if (dataKind == DBPDataKind.NUMERIC || dataKind == DBPDataKind.DATETIME || dataKind == DBPDataKind.STRING) {
operators.add(DBCLogicalOperator.EQUALS);
operators.add(DBCLogicalOperator.NOT_EQUALS);
operators.add(DBCLogicalOperator.GREATER);
//operators.add(DBCLogicalOperator.GREATER_EQUALS);
operators.add(DBCLogicalOperator.LESS);
//operators.add(DBCLogicalOperator.LESS_EQUALS);
operators.add(DBCLogicalOperator.IN);
}
if (dataKind == DBPDataKind.STRING) {
operators.add(DBCLogicalOperator.LIKE);
}
return operators.toArray(new DBCLogicalOperator[operators.size()]);
}
public static Object getRawValue(Object value) {
if (value instanceof DBDValue) {
return ((DBDValue)value).getRawValue();
} else {
return value;
}
}
public static boolean isIndexedAttribute(DBRProgressMonitor monitor, DBSEntityAttribute attribute) throws DBException {
DBSEntity entity = attribute.getParentObject();
if (entity instanceof DBSTable) {
Collection<? extends DBSTableIndex> indexes = ((DBSTable) entity).getIndexes(monitor);
if (!CommonUtils.isEmpty(indexes)) {
for (DBSTableIndex index : indexes) {
if (getConstraintAttribute(monitor, index, attribute) != null) {
return true;
}
}
}
}
return false;
}
@Nullable
public static DBCTransactionManager getTransactionManager(@Nullable DBCExecutionContext executionContext) {
if (executionContext != null && executionContext.isConnected()) {
return getAdapter(DBCTransactionManager.class, executionContext);
}
return null;
}
@SuppressWarnings("unchecked")
@NotNull
public static <T> Class<T> getDriverClass(@NotNull DBPDataSource dataSource, @NotNull String className) throws ClassNotFoundException {
return (Class<T>) Class.forName(className, true, dataSource.getContainer().getDriver().getClassLoader());
}
@SuppressWarnings("unchecked")
@NotNull
public static <T extends DBCSession> T openMetaSession(@NotNull DBRProgressMonitor monitor, @NotNull DBPDataSource dataSource, @NotNull String task) {
return (T) dataSource.getDefaultContext(true).openSession(monitor, DBCExecutionPurpose.META, task);
}
@SuppressWarnings("unchecked")
@NotNull
public static <T extends DBCSession> T openUtilSession(@NotNull DBRProgressMonitor monitor, @NotNull DBPDataSource dataSource, @NotNull String task) {
return (T) dataSource.getDefaultContext(false).openSession(monitor, DBCExecutionPurpose.UTIL, task);
}
@Nullable
public static DBSObject getFromObject(Object object) {
if (object instanceof DBSWrapper) {
return ((DBSWrapper) object).getObject();
} else if (object instanceof DBSObject) {
return (DBSObject) object;
} else {
return null;
}
}
public static boolean isAtomicParameter(Object o) {
return o == null || o instanceof CharSequence || o instanceof Number || o instanceof java.util.Date || o instanceof Boolean;
}
@NotNull
public static DBSObject getDefaultOrActiveObject(@NotNull DBSInstance object)
{
DBSObject selectedObject = getActiveInstanceObject(object);
return selectedObject == null ? object : selectedObject;
}
@Nullable
public static DBSObject getActiveInstanceObject(@NotNull DBSInstance object)
{
return getSelectedObject(object, true);
}
@Nullable
public static DBSObject getSelectedObject(@NotNull DBSObject object, boolean searchNested)
{
DBSObjectSelector objectSelector = getAdapter(DBSObjectSelector.class, object);
if (objectSelector != null) {
DBSObject selectedObject1 = objectSelector.getDefaultObject();
if (searchNested && selectedObject1 != null) {
DBSObject nestedObject = getSelectedObject(selectedObject1, true);
if (nestedObject != null) {
return nestedObject;
}
}
return selectedObject1;
}
return null;
}
@NotNull
public static DBSObject[] getSelectedObjects(@NotNull DBSObject object)
{
DBSObjectSelector objectSelector = getAdapter(DBSObjectSelector.class, object);
if (objectSelector != null) {
DBSObject selectedObject1 = objectSelector.getDefaultObject();
if (selectedObject1 != null) {
DBSObject nestedObject = getSelectedObject(selectedObject1, true);
if (nestedObject != null) {
return new DBSObject[] { selectedObject1, nestedObject };
} else {
return new DBSObject[] { selectedObject1 };
}
}
}
return new DBSObject[0];
}
public static boolean isHiddenObject(Object object) {
return object instanceof DBPHiddenObject && ((DBPHiddenObject) object).isHidden();
}
public static DBDPseudoAttribute getRowIdAttribute(DBSEntity entity) {
if (entity instanceof DBDPseudoAttributeContainer) {
try {
return DBDPseudoAttribute.getAttribute(
((DBDPseudoAttributeContainer) entity).getPseudoAttributes(),
DBDPseudoAttributeType.ROWID);
} catch (DBException e) {
log.warn("Can't get pseudo attributes for '" + entity.getName() + "'", e);
}
}
return null;
}
public static DBDPseudoAttribute getPseudoAttribute(DBSEntity entity, String attrName) {
if (entity instanceof DBDPseudoAttributeContainer) {
try {
DBDPseudoAttribute[] pseudoAttributes = ((DBDPseudoAttributeContainer) entity).getPseudoAttributes();
if (pseudoAttributes != null && pseudoAttributes.length > 0) {
for (int i = 0; i < pseudoAttributes.length; i++) {
DBDPseudoAttribute pa = pseudoAttributes[i];
String attrId = pa.getAlias();
if (CommonUtils.isEmpty(attrId)) {
attrId = pa.getName();
}
if (attrId.equals(attrName)) {
return pa;
}
}
}
} catch (DBException e) {
log.warn("Can't get pseudo attributes for '" + entity.getName() + "'", e);
}
}
return null;
}
public static boolean isPseudoAttribute(DBSAttributeBase attr) {
return attr instanceof DBDAttributeBinding && ((DBDAttributeBinding) attr).isPseudoAttribute();
}
public static <TYPE extends DBPNamedObject> Comparator<TYPE> nameComparator()
{
return new Comparator<TYPE>() {
@Override
public int compare(DBPNamedObject o1, DBPNamedObject o2)
{
return o1.getName().compareTo(o2.getName());
}
};
}
public static Comparator<? super DBSAttributeBase> orderComparator() {
return new Comparator<DBSAttributeBase>() {
@Override
public int compare(DBSAttributeBase o1, DBSAttributeBase o2) {
return o1.getOrdinalPosition() - o2.getOrdinalPosition();
}
};
}
public static <T extends DBPNamedObject> void orderObjects(@NotNull List<T> objects)
{
Collections.sort(objects, new Comparator<T>() {
@Override
public int compare(T o1, T o2) {
String name1 = o1.getName();
String name2 = o2.getName();
return name1 == null && name2 == null ? 0 :
(name1 == null ? -1 :
(name2 == null ? 1 : name1.compareTo(name2)));
}
});
}
public static String getClientApplicationName(DBPDataSourceContainer container, String purpose) {
if (container.getPreferenceStore().getBoolean(ModelPreferences.META_CLIENT_NAME_OVERRIDE)) {
return container.getPreferenceStore().getString(ModelPreferences.META_CLIENT_NAME_VALUE);
}
final String productTitle = GeneralUtils.getProductTitle();
return purpose == null ? productTitle : productTitle + " - " + purpose;
}
@NotNull
public static DBPErrorAssistant.ErrorType discoverErrorType(@NotNull DBPDataSource dataSource, @NotNull Throwable error) {
DBPErrorAssistant errorAssistant = getAdapter(DBPErrorAssistant.class, dataSource);
if (errorAssistant != null) {
return ((DBPErrorAssistant) dataSource).discoverErrorType(error);
}
return DBPErrorAssistant.ErrorType.NORMAL;
}
}