/* * 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.generic.model; 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.ext.generic.GenericConstants; import org.jkiss.dbeaver.ext.generic.model.meta.GenericMetaModel; import org.jkiss.dbeaver.ext.generic.model.meta.GenericMetaObject; import org.jkiss.dbeaver.model.*; import org.jkiss.dbeaver.model.connection.DBPConnectionConfiguration; import org.jkiss.dbeaver.model.connection.DBPDriver; import org.jkiss.dbeaver.model.data.DBDValueHandlerProvider; import org.jkiss.dbeaver.model.exec.*; import org.jkiss.dbeaver.model.exec.jdbc.*; import org.jkiss.dbeaver.model.exec.plan.DBCQueryPlanner; import org.jkiss.dbeaver.model.impl.jdbc.*; import org.jkiss.dbeaver.model.impl.jdbc.cache.JDBCBasicDataTypeCache; import org.jkiss.dbeaver.model.impl.jdbc.cache.JDBCObjectCache; import org.jkiss.dbeaver.model.meta.Association; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor; import org.jkiss.dbeaver.model.sql.SQLDialect; import org.jkiss.dbeaver.model.struct.*; import org.jkiss.utils.CommonUtils; import org.jkiss.utils.time.ExtendedDateFormat; import java.sql.*; import java.text.Format; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Properties; import java.util.regex.Matcher; /** * GenericDataSource */ public class GenericDataSource extends JDBCDataSource implements DBSObjectSelector, DBPTermProvider, IAdaptable, GenericStructContainer { private static final Log log = Log.getLog(GenericDataSource.class); static boolean populateClientAppName = false; private final TableTypeCache tableTypeCache; private final JDBCBasicDataTypeCache dataTypeCache; private List<GenericCatalog> catalogs; private List<GenericSchema> schemas; private final GenericMetaModel metaModel; private GenericObjectContainer structureContainer; private String queryGetActiveDB; private String querySetActiveDB; private String selectedEntityType; private String selectedEntityName; private boolean selectedEntityFromAPI; private String allObjectsPattern; private boolean supportsStructCache; private DBCQueryPlanner queryPlanner; private Format nativeFormatTimestamp, nativeFormatTime, nativeFormatDate; public GenericDataSource(@NotNull DBRProgressMonitor monitor, @NotNull DBPDataSourceContainer container, @NotNull GenericMetaModel metaModel, @NotNull SQLDialect dialect) throws DBException { super(monitor, container, dialect, false); this.metaModel = metaModel; final DBPDriver driver = container.getDriver(); this.dataTypeCache = metaModel.createDataTypeCache(container); this.tableTypeCache = new TableTypeCache(); this.queryGetActiveDB = CommonUtils.toString(driver.getDriverParameter(GenericConstants.PARAM_QUERY_GET_ACTIVE_DB)); this.querySetActiveDB = CommonUtils.toString(driver.getDriverParameter(GenericConstants.PARAM_QUERY_SET_ACTIVE_DB)); this.selectedEntityType = CommonUtils.toString(driver.getDriverParameter(GenericConstants.PARAM_ACTIVE_ENTITY_TYPE)); if (CommonUtils.isEmpty(this.selectedEntityType)) { this.selectedEntityType = null; } this.allObjectsPattern = CommonUtils.toString(driver.getDriverParameter(GenericConstants.PARAM_ALL_OBJECTS_PATTERN)); if (CommonUtils.isEmpty(this.allObjectsPattern)) { this.allObjectsPattern = "%"; } else if ("null".equalsIgnoreCase(this.allObjectsPattern)) { this.allObjectsPattern = null; } // Init native formats nativeFormatTimestamp = makeNativeFormat(GenericConstants.PARAM_NATIVE_FORMAT_TIMESTAMP); nativeFormatTime = makeNativeFormat(GenericConstants.PARAM_NATIVE_FORMAT_TIME); nativeFormatDate = makeNativeFormat(GenericConstants.PARAM_NATIVE_FORMAT_DATE); initializeMainContext(monitor); } @Override protected String getConnectionURL(DBPConnectionConfiguration connectionInfo) { // Recreate URL from parameters // Driver settings and URL template may have change since connection creation String connectionURL = getContainer().getDriver().getDataSourceProvider().getConnectionURL(getContainer().getDriver(), connectionInfo); if (connectionInfo.getUrl() != null && !CommonUtils.equalObjects(connectionURL, connectionInfo.getUrl())) { log.warn("Actual connection URL (" + connectionURL + ") differs from previously saved (" + connectionInfo.getUrl() + "). " + "Probably driver properties were changed. Please go to the connection '" + getContainer().getName() + "' editor."); connectionInfo.setUrl(connectionURL); } return connectionURL; } @Override protected Connection openConnection(@NotNull DBRProgressMonitor monitor, @NotNull String purpose) throws DBCException { Connection jdbcConnection = super.openConnection(monitor, purpose); if (populateClientAppName) { // Provide client info // "ApplicationName" property seems to be pretty standard try { final ResultSet ciList = jdbcConnection.getMetaData().getClientInfoProperties(); if (ciList != null) { try { while (ciList.next()) { final String name = JDBCUtils.safeGetString(ciList, "NAME"); final int maxLength = JDBCUtils.safeGetInt(ciList, "MAX_LEN"); if ("ApplicationName".equals(name)) { String appName = DBUtils.getClientApplicationName(getContainer(), purpose); if (maxLength > 0) { appName = CommonUtils.truncateString(appName, maxLength <= 0 ? 48 : maxLength); } jdbcConnection.setClientInfo("ApplicationName", appName); break; } } } finally { ciList.close(); } } } catch (Throwable e) { // just ignore } } return jdbcConnection; } protected void initializeContextState(@NotNull DBRProgressMonitor monitor, @NotNull JDBCExecutionContext context, boolean setActiveObject) throws DBCException { if (setActiveObject) { setActiveEntityName(monitor, context, getDefaultObject()); } } public String getAllObjectsPattern() { return allObjectsPattern; } @NotNull public GenericMetaModel getMetaModel() { return metaModel; } @Nullable public GenericMetaObject getMetaObject(String id) { return metaModel.getMetaObject(id); } @Override protected DBPDataSourceInfo createDataSourceInfo(@NotNull JDBCDatabaseMetaData metaData) { final GenericDataSourceInfo info = new GenericDataSourceInfo(getContainer().getDriver(), metaData); final JDBCSQLDialect dialect = (JDBCSQLDialect)getSQLDialect(); final Object supportsReferences = getContainer().getDriver().getDriverParameter(GenericConstants.PARAM_SUPPORTS_REFERENCES); if (supportsReferences != null) { info.setSupportsReferences(Boolean.valueOf(supportsReferences.toString())); } final Object supportsIndexes = getContainer().getDriver().getDriverParameter(GenericConstants.PARAM_SUPPORTS_INDEXES); if (supportsIndexes != null) { info.setSupportsIndexes(Boolean.valueOf(supportsIndexes.toString())); } final Object supportsStoredCode = getContainer().getDriver().getDriverParameter(GenericConstants.PARAM_SUPPORTS_STORED_CODE); if (supportsStoredCode != null) { info.setSupportsStoredCode(Boolean.valueOf(supportsStoredCode.toString())); } final Object supportsSubqueries = getContainer().getDriver().getDriverParameter(GenericConstants.PARAM_SUPPORTS_SUBQUERIES); if (supportsSubqueries != null) { dialect.setSupportsSubqueries(Boolean.valueOf(supportsSubqueries.toString())); } final Object supportsStructCacheParam = getContainer().getDriver().getDriverParameter(GenericConstants.PARAM_SUPPORTS_STRUCT_CACHE); if (supportsStructCacheParam != null) { this.supportsStructCache = CommonUtils.toBoolean(supportsStructCacheParam); } return info; } @Override public void shutdown(DBRProgressMonitor monitor) { super.shutdown(monitor); String paramShutdown = CommonUtils.toString(getContainer().getDriver().getDriverParameter(GenericConstants.PARAM_SHUTDOWN_URL_PARAM)); if (!CommonUtils.isEmpty(paramShutdown)) { monitor.subTask("Shutdown embedded database"); try { Properties shutdownProps = new Properties(); DBPConnectionConfiguration connectionInfo = getContainer().getActualConnectionConfiguration(); if (!CommonUtils.isEmpty(connectionInfo.getUserName())) { shutdownProps.put(DBConstants.DATA_SOURCE_PROPERTY_USER, getConnectionUserName(connectionInfo)); } if (!CommonUtils.isEmpty(connectionInfo.getUserPassword())) { shutdownProps.put(DBConstants.DATA_SOURCE_PROPERTY_PASSWORD, getConnectionUserPassword(connectionInfo)); } final Driver driver = getDriverInstance(new VoidProgressMonitor()); // Use void monitor - driver already loaded if (driver != null) { driver.connect( getContainer().getActualConnectionConfiguration().getUrl() + paramShutdown, shutdownProps.isEmpty() ? null : shutdownProps); } } catch (Exception e) { log.debug("Shutdown finished: :" + e.getMessage()); } monitor.worked(1); } } boolean supportsStructCache() { return supportsStructCache; } @Association public Collection<GenericTableType> getTableTypes(DBRProgressMonitor monitor) throws DBException { return tableTypeCache.getAllObjects(monitor, this); } public Collection<GenericCatalog> getCatalogs() { return catalogs; } public GenericCatalog getCatalog(String name) { return DBUtils.findObject(getCatalogs(), name); } @Association public Collection<GenericSchema> getSchemas() { return schemas; } public GenericSchema getSchema(String name) { return DBUtils.findObject(getSchemas(), name); } @NotNull @Override public GenericDataSource getDataSource() { return this; } @Override public DBSObject getObject() { return getContainer(); } @Override public GenericCatalog getCatalog() { return null; } @Override public GenericSchema getSchema() { return null; } @Override public TableCache getTableCache() { return structureContainer.getTableCache(); } @Override public IndexCache getIndexCache() { return structureContainer.getIndexCache(); } @Override public PrimaryKeysCache getPrimaryKeysCache() { return structureContainer.getPrimaryKeysCache(); } @Override public ForeignKeysCache getForeignKeysCache() { return structureContainer.getForeignKeysCache(); } @Override public Collection<GenericTable> getViews(DBRProgressMonitor monitor) throws DBException { return structureContainer == null ? null : structureContainer.getViews(monitor); } @Override public Collection<GenericTable> getPhysicalTables(DBRProgressMonitor monitor) throws DBException { return structureContainer == null ? null : structureContainer.getPhysicalTables(monitor); } @Override public Collection<GenericTable> getTables(DBRProgressMonitor monitor) throws DBException { return structureContainer == null ? null : structureContainer.getTables(monitor); } @Override public GenericTable getTable(DBRProgressMonitor monitor, String name) throws DBException { return structureContainer == null ? null : structureContainer.getTable(monitor, name); } @Override public Collection<GenericPackage> getPackages(DBRProgressMonitor monitor) throws DBException { return structureContainer == null ? null : structureContainer.getPackages(monitor); } @Override public Collection<GenericTableIndex> getIndexes(DBRProgressMonitor monitor) throws DBException { return structureContainer == null ? null : structureContainer.getIndexes(monitor); } @Override public Collection<GenericProcedure> getProcedures(DBRProgressMonitor monitor) throws DBException { return structureContainer == null ? null : structureContainer.getProcedures(monitor); } @Override public GenericProcedure getProcedure(DBRProgressMonitor monitor, String uniqueName) throws DBException { return structureContainer == null ? null : structureContainer.getProcedure(monitor, uniqueName); } @Override public Collection<GenericProcedure> getProcedures(DBRProgressMonitor monitor, String name) throws DBException { return structureContainer == null ? null : structureContainer.getProcedures(monitor, name); } @Override public Collection<GenericSequence> getSequences(DBRProgressMonitor monitor) throws DBException { return structureContainer == null ? null : structureContainer.getSequences(monitor); } @Override public Collection<? extends GenericTrigger> getTriggers(DBRProgressMonitor monitor) throws DBException { return structureContainer == null ? null : structureContainer.getTriggers(monitor); } @Override public void initialize(@NotNull DBRProgressMonitor monitor) throws DBException { super.initialize(monitor); Object omitCatalog = getContainer().getDriver().getDriverParameter(GenericConstants.PARAM_OMIT_CATALOG); Object omitTypeCache = getContainer().getDriver().getDriverParameter(GenericConstants.PARAM_OMIT_TYPE_CACHE); if (omitTypeCache == null || !CommonUtils.toBoolean(omitTypeCache)) { // Cache data types try { dataTypeCache.getAllObjects(monitor, this); } catch (DBException e) { log.warn("Can't fetch database data types", e); } } else { // Use basic data types dataTypeCache.fillStandardTypes(this); } try (JDBCSession session = DBUtils.openMetaSession(monitor, this, "Read generic metadata")) { // Read metadata JDBCDatabaseMetaData metaData = session.getMetaData(); boolean catalogsFiltered = false; if (omitCatalog == null || !CommonUtils.toBoolean(omitCatalog)) { // Read catalogs monitor.subTask("Extract catalogs"); monitor.worked(1); final GenericMetaObject catalogObject = getMetaObject(GenericConstants.OBJECT_CATALOG); final DBSObjectFilter catalogFilters = getContainer().getObjectFilter(GenericCatalog.class, null, false); final List<String> catalogNames = new ArrayList<>(); try { try (JDBCResultSet dbResult = metaData.getCatalogs()) { int totalCatalogs = 0; while (dbResult.next()) { String catalogName = GenericUtils.safeGetString(catalogObject, dbResult, JDBCConstants.TABLE_CAT); if (CommonUtils.isEmpty(catalogName)) { // Some drivers uses TABLE_QUALIFIER instead of catalog catalogName = GenericUtils.safeGetStringTrimmed(catalogObject, dbResult, JDBCConstants.TABLE_QUALIFIER); if (CommonUtils.isEmpty(catalogName)) { continue; } } totalCatalogs++; if (catalogFilters == null || catalogFilters.matches(catalogName)) { catalogNames.add(catalogName); monitor.subTask("Extract catalogs - " + catalogName); } else { catalogsFiltered = true; } if (monitor.isCanceled()) { break; } } if (totalCatalogs == 1) { // Just one catalog. Looks like DB2 or PostgreSQL // Let's just skip it and use only schemas // It's ok to use "%" instead of catalog name anyway catalogNames.clear(); } } } catch (UnsupportedOperationException | SQLFeatureNotSupportedException e) { // Just skip it log.debug(e); } catch (SQLException e) { // Error reading catalogs - just warn about it log.warn("Can't read catalog list", e); } if (!catalogNames.isEmpty() || catalogsFiltered) { this.catalogs = new ArrayList<>(); for (String catalogName : catalogNames) { GenericCatalog catalog = new GenericCatalog(this, catalogName); this.catalogs.add(catalog); } } } if (CommonUtils.isEmpty(catalogs) && !catalogsFiltered) { // Catalogs not supported - try to read root schemas monitor.subTask("Extract schemas"); monitor.worked(1); List<GenericSchema> tmpSchemas = loadSchemas(session, null); if (tmpSchemas != null) { this.schemas = tmpSchemas; } if (CommonUtils.isEmpty(schemas)) { this.structureContainer = new DataSourceObjectContainer(); } } determineSelectedEntity(session); } catch (SQLException ex) { throw new DBException("Error reading metadata", ex, this); } } List<GenericSchema> loadSchemas(JDBCSession session, GenericCatalog catalog) throws DBException { try { final GenericMetaObject schemaObject = getMetaObject(GenericConstants.OBJECT_SCHEMA); final DBSObjectFilter schemaFilters = getContainer().getObjectFilter(GenericSchema.class, null, false); final List<GenericSchema> tmpSchemas = new ArrayList<>(); JDBCResultSet dbResult = null; boolean catalogSchemas = false; if (catalog != null) { try { dbResult = session.getMetaData().getSchemas( catalog.getName(), schemaFilters != null && schemaFilters.hasSingleMask() ? schemaFilters.getSingleMask() : getAllObjectsPattern()); catalogSchemas = true; } catch (Throwable e) { // This method not supported (may be old driver version) // Use general schema reading method log.debug("Error reading schemas in catalog '" + catalog.getName() + "' - " + e.getMessage()); } } if (dbResult == null) { dbResult = session.getMetaData().getSchemas(); } try { while (dbResult.next()) { if (session.getProgressMonitor().isCanceled()) { break; } String schemaName = GenericUtils.safeGetString(schemaObject, dbResult, JDBCConstants.TABLE_SCHEM); if (CommonUtils.isEmpty(schemaName)) { // some drivers uses TABLE_OWNER column instead of TABLE_SCHEM schemaName = GenericUtils.safeGetString(schemaObject, dbResult, JDBCConstants.TABLE_OWNER); } if (CommonUtils.isEmpty(schemaName)) { continue; } if (schemaFilters != null && !schemaFilters.matches(schemaName)) { // Doesn't match filter continue; } String catalogName = GenericUtils.safeGetString(schemaObject, dbResult, JDBCConstants.TABLE_CATALOG); if (!CommonUtils.isEmpty(catalogName)) { if (catalog == null) { // Invalid schema's catalog or schema without catalog (then do not use schemas as structure) log.debug("Catalog name (" + catalogName + ") found for schema '" + schemaName + "' while schema doesn't have parent catalog"); } else if (!catalog.getName().equals(catalogName)) { if (!catalogSchemas) { // Just skip it - we have list of all existing schemas and this one belongs to another catalog continue; } log.debug("Catalog name '" + catalogName + "' differs from schema's catalog '" + catalog.getName() + "'"); } } session.getProgressMonitor().subTask("Schema " + schemaName); GenericSchema schema; if (catalog == null) { schema = new GenericSchema(this, schemaName); } else { schema = new GenericSchema(catalog, schemaName); } tmpSchemas.add(schema); } } finally { dbResult.close(); } if (catalog == null && tmpSchemas.size() == 1 && (schemaFilters == null || schemaFilters.isNotApplicable())) { // Only one schema and no catalogs // Most likely it is a fake one, let's skip it // Anyway using "%" instead is ok tmpSchemas.clear(); } return tmpSchemas; } catch (UnsupportedOperationException | SQLFeatureNotSupportedException e) { // Schemas are not supported log.debug(e); return null; } catch (Exception ex) { // Schemas do not supported - just ignore this error log.warn("Can't read schema list", ex); return null; } } @Override public DBSObject refreshObject(@NotNull DBRProgressMonitor monitor) throws DBException { super.refreshObject(monitor); this.selectedEntityName = null; this.structureContainer = null; this.tableTypeCache.clearCache(); this.catalogs = null; this.schemas = null; this.initialize(monitor); return this; } @Nullable @Override public DBCQueryTransformer createQueryTransformer(@NotNull DBCQueryTransformType type) { if (metaModel instanceof DBCQueryTransformProvider) { DBCQueryTransformer transformer = ((DBCQueryTransformProvider) metaModel).createQueryTransformer(type); if (transformer != null) { return transformer; } } return super.createQueryTransformer(type); } GenericTable findTable(@NotNull DBRProgressMonitor monitor, String catalogName, String schemaName, String tableName) throws DBException { GenericObjectContainer container = null; if (!CommonUtils.isEmpty(catalogName) && !CommonUtils.isEmpty(catalogs)) { container = getCatalog(catalogName); if (container == null) { log.error("Catalog " + catalogName + " not found"); return null; } } if (!CommonUtils.isEmpty(schemaName)) { if (container != null) { container = ((GenericCatalog)container).getSchema(monitor, schemaName); } else if (!CommonUtils.isEmpty(schemas)) { container = this.getSchema(schemaName); } else { container = structureContainer; } if (container == null) { log.debug("Schema '" + schemaName + "' not found"); return null; } } if (container == null) { container = structureContainer; } return container.getTable(monitor, tableName); } @Override public Collection<? extends DBSObject> getChildren(@NotNull DBRProgressMonitor monitor) throws DBException { if (!CommonUtils.isEmpty(getCatalogs())) { return getCatalogs(); } else if (!CommonUtils.isEmpty(getSchemas())) { return getSchemas(); } else if (structureContainer != null) { return structureContainer.getTables(monitor); } else { return null; } } @Override public DBSObject getChild(@NotNull DBRProgressMonitor monitor, @NotNull String childName) throws DBException { if (!CommonUtils.isEmpty(getCatalogs())) { return getCatalog(childName); } else if (!CommonUtils.isEmpty(getSchemas())) { return getSchema(childName); } else if (structureContainer != null) { return structureContainer.getChild(monitor, childName); } else { return null; } } @Override public Class<? extends DBSObject> getChildType(@NotNull DBRProgressMonitor monitor) throws DBException { if (!CommonUtils.isEmpty(catalogs)) { return GenericCatalog.class; } else if (!CommonUtils.isEmpty(schemas)) { return GenericSchema.class; } else { return GenericTable.class; } } @Override public void cacheStructure(@NotNull DBRProgressMonitor monitor, int scope) throws DBException { if (!CommonUtils.isEmpty(catalogs)) { for (GenericCatalog catalog : catalogs) catalog.cacheStructure(monitor, scope); } else if (!CommonUtils.isEmpty(schemas)) { for (GenericSchema schema : schemas) schema.cacheStructure(monitor, scope); } else if (structureContainer != null) { structureContainer.cacheStructure(monitor, scope); } } private boolean isChild(DBSObject object) throws DBException { if (object instanceof GenericCatalog) { return !CommonUtils.isEmpty(catalogs) && catalogs.contains(GenericCatalog.class.cast(object)); } else if (object instanceof GenericSchema) { return !CommonUtils.isEmpty(schemas) && schemas.contains(GenericSchema.class.cast(object)); } return false; } @Override public boolean supportsDefaultChange() { if (selectedEntityFromAPI) { return true; } if (!CommonUtils.isEmpty(querySetActiveDB)) { if (CommonUtils.isEmpty(selectedEntityType)) { return !CommonUtils.isEmpty(getCatalogs()) || !CommonUtils.isEmpty(getSchemas()); } if (!CommonUtils.isEmpty(getCatalogs())) { return GenericConstants.ENTITY_TYPE_CATALOG.equals(selectedEntityType); } else if (!CommonUtils.isEmpty(getSchemas())) { return GenericConstants.ENTITY_TYPE_SCHEMA.equals(selectedEntityType); } } return false; } @Override public DBSObject getDefaultObject() { if (!CommonUtils.isEmpty(selectedEntityName)) { if (!CommonUtils.isEmpty(catalogs)) { if (selectedEntityType == null || selectedEntityType.equals(GenericConstants.ENTITY_TYPE_CATALOG)) { return getCatalog(selectedEntityName); } } else if (!CommonUtils.isEmpty(schemas)) { if (selectedEntityType == null || selectedEntityType.equals(GenericConstants.ENTITY_TYPE_SCHEMA)) { return getSchema(selectedEntityName); } } } return null; } @Override public void setDefaultObject(@NotNull DBRProgressMonitor monitor, @NotNull DBSObject object) throws DBException { final DBSObject oldSelectedEntity = getDefaultObject(); // Check removed because we can select the same object on invalidate // if (object == oldSelectedEntity) { // return; // } if (!isChild(object)) { throw new DBException("Bad child object specified as active: " + object); } for (JDBCExecutionContext context : getAllContexts()) { setActiveEntityName(monitor, context, object); } if (oldSelectedEntity != null) { DBUtils.fireObjectSelect(oldSelectedEntity, false); } DBUtils.fireObjectSelect(object, true); } @Override public boolean refreshDefaultObject(@NotNull DBCSession session) throws DBException { String oldDefaultObject = selectedEntityName; determineSelectedEntity((JDBCSession) session); if (!CommonUtils.equalObjects(oldDefaultObject, selectedEntityName)) { final DBSObject newDefaultObject = getDefaultObject(); if (newDefaultObject != null) { setDefaultObject(session.getProgressMonitor(), newDefaultObject); return true; } } return false; } String getSelectedEntityType() { return selectedEntityType; } String getSelectedEntityName() { return selectedEntityName; } private void determineSelectedEntity(JDBCSession session) { // Get selected entity (catalog or schema) selectedEntityName = null; if (CommonUtils.isEmpty(queryGetActiveDB)) { try { selectedEntityName = session.getCatalog(); if (selectedEntityType == null && !CommonUtils.isEmpty(selectedEntityName)) { selectedEntityType = GenericConstants.ENTITY_TYPE_CATALOG; selectedEntityFromAPI = true; } } catch (SQLException e) { // Seems to be not supported log.debug(e); } if (CommonUtils.isEmpty(selectedEntityName)) { // Try to use current schema try { selectedEntityName = session.getSchema(); if (selectedEntityType == null && !CommonUtils.isEmpty(selectedEntityName)) { selectedEntityType = GenericConstants.ENTITY_TYPE_SCHEMA; selectedEntityFromAPI = true; } } catch (SQLException e) { log.debug(e); } } if (CommonUtils.isEmpty(selectedEntityName)) { // If we have only one catalog then it is our selected entity if (!CommonUtils.isEmpty(catalogs) && catalogs.size() == 1) { selectedEntityType = GenericConstants.ENTITY_TYPE_CATALOG; selectedEntityName = catalogs.get(0).getName(); } else if (!CommonUtils.isEmpty(schemas) && schemas.size() == 1) { selectedEntityType = GenericConstants.ENTITY_TYPE_SCHEMA; selectedEntityName = schemas.get(0).getName(); } } } else { try { try (JDBCPreparedStatement dbStat = session.prepareStatement(queryGetActiveDB)) { try (JDBCResultSet resultSet = dbStat.executeQuery()) { resultSet.next(); selectedEntityName = JDBCUtils.safeGetStringTrimmed(resultSet, 1); if (!CommonUtils.isEmpty(selectedEntityName)) { // [PostgreSQL] int divPos = selectedEntityName.lastIndexOf(','); if (divPos != -1) { selectedEntityName = selectedEntityName.substring(divPos + 1); } } } } } catch (SQLException e) { log.debug(e); selectedEntityName = null; } } } void setActiveEntityName(DBRProgressMonitor monitor, JDBCExecutionContext context, DBSObject entity) throws DBCException { if (entity == null) { log.debug("Null current entity"); return; } try (JDBCSession session = context.openSession(monitor, DBCExecutionPurpose.UTIL, "Set active catalog")) { if (selectedEntityFromAPI) { // Use JDBC API to change entity switch (selectedEntityType) { case GenericConstants.ENTITY_TYPE_CATALOG: session.setCatalog(entity.getName()); break; case GenericConstants.ENTITY_TYPE_SCHEMA: session.setSchema(entity.getName()); break; default: throw new DBCException("No API to change active entity if type '" + selectedEntityType + "'"); } } else { if (CommonUtils.isEmpty(querySetActiveDB) || !(entity instanceof GenericObjectContainer)) { throw new DBCException("Active database can't be changed for this kind of datasource!"); } String changeQuery = querySetActiveDB.replaceFirst("\\?", Matcher.quoteReplacement(entity.getName())); try (JDBCPreparedStatement dbStat = session.prepareStatement(changeQuery)) { dbStat.execute(); } } } catch (SQLException e) { throw new DBCException(e, context.getDataSource()); } selectedEntityName = entity.getName(); } @Override public <T> T getAdapter(Class<T> adapter) { if (adapter == DBSStructureAssistant.class) { return adapter.cast(new GenericStructureAssistant(this)); } else if (adapter == DBCQueryPlanner.class) { if (queryPlanner == null) { queryPlanner = metaModel.getQueryPlanner(this); } return adapter.cast(queryPlanner); } else if (adapter == DBDValueHandlerProvider.class) { if (metaModel instanceof DBDValueHandlerProvider) { return adapter.cast(metaModel); } } return super.getAdapter(adapter); } @Override public String getObjectTypeTerm(String path, String objectType, boolean multiple) { String term = null; if (GenericConstants.TERM_CATALOG.equals(objectType)) { term = getInfo().getCatalogTerm(); } else if (GenericConstants.TERM_SCHEMA.equals(objectType)) { term = getInfo().getSchemaTerm(); } else if (GenericConstants.TERM_PROCEDURE.equals(objectType)) { term = getInfo().getProcedureTerm(); } if (term != null && multiple) { term += "s"; } return term; } @Nullable @Override public ErrorPosition[] getErrorPosition(@NotNull DBRProgressMonitor monitor, @NotNull DBCExecutionContext context, @NotNull String query, @NotNull Throwable error) { ErrorPosition position = metaModel.getErrorPosition(error); return position == null ? null : new ErrorPosition[] { position }; } @Override public Collection<? extends DBSDataType> getLocalDataTypes() { return dataTypeCache.getCachedObjects(); } @Override public DBSDataType getLocalDataType(String typeName) { return dataTypeCache.getCachedObject(typeName); } @NotNull public DBPDataKind resolveDataKind(@NotNull String typeName, int valueType) { DBSDataType dataType = getLocalDataType(typeName); if (dataType != null) { return super.resolveDataKind(dataType.getTypeName(), dataType.getTypeID()); } return super.resolveDataKind(typeName, valueType); } // Native formats public Format getNativeFormatTimestamp() { return nativeFormatTimestamp; } public Format getNativeFormatTime() { return nativeFormatTime; } public Format getNativeFormatDate() { return nativeFormatDate; } private Format makeNativeFormat(String paramName) { Object param = getContainer().getDriver().getDriverParameter(paramName); if (param == null) { return null; } try { return new ExtendedDateFormat(CommonUtils.toString(param)); } catch (Exception e) { log.error(e); return null; } } private class TableTypeCache extends JDBCObjectCache<GenericDataSource, GenericTableType> { @Override protected JDBCStatement prepareObjectsStatement(@NotNull JDBCSession session, @NotNull GenericDataSource owner) throws SQLException { return session.getMetaData().getTableTypes().getSourceStatement(); } @Override protected GenericTableType fetchObject(@NotNull JDBCSession session, @NotNull GenericDataSource owner, @NotNull JDBCResultSet resultSet) throws SQLException, DBException { return new GenericTableType( GenericDataSource.this, GenericUtils.safeGetString( getMetaObject(GenericConstants.OBJECT_TABLE_TYPE), resultSet, JDBCConstants.TABLE_TYPE)); } } private class DataSourceObjectContainer extends GenericObjectContainer { private DataSourceObjectContainer() { super(GenericDataSource.this); } @Override public GenericCatalog getCatalog() { return null; } @Override public GenericSchema getSchema() { return null; } @Override public DBSObject getObject() { return GenericDataSource.this.getContainer(); } @Override public Class<? extends DBSEntity> getChildType(@NotNull DBRProgressMonitor monitor) throws DBException { return GenericTable.class; } @NotNull @Override public String getName() { return GenericDataSource.this.getName(); } @Nullable @Override public String getDescription() { return GenericDataSource.this.getDescription(); } @Override public DBSObject getParentObject() { return GenericDataSource.this.getParentObject(); } } }