/* * 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.jkiss.code.NotNull; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBPEvaluationContext; import org.jkiss.dbeaver.model.DBPRefreshableObject; import org.jkiss.dbeaver.model.DBUtils; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.struct.DBSObject; import org.jkiss.utils.CommonUtils; import java.util.ArrayList; import java.util.Collection; import java.util.List; /** * GenericEntityContainer */ public abstract class GenericObjectContainer implements GenericStructContainer,DBPRefreshableObject { private static final Log log = Log.getLog(GenericObjectContainer.class); @NotNull private final GenericDataSource dataSource; private final TableCache tableCache; private final IndexCache indexCache; private final ForeignKeysCache foreignKeysCache; private final PrimaryKeysCache primaryKeysCache; private List<GenericPackage> packages; protected List<GenericProcedure> procedures; protected List<GenericSequence> sequences; private List<? extends GenericTrigger> triggers; protected GenericObjectContainer(@NotNull GenericDataSource dataSource) { this.dataSource = dataSource; this.tableCache = new TableCache(dataSource); this.indexCache = new IndexCache(tableCache); this.primaryKeysCache = new PrimaryKeysCache(tableCache); this.foreignKeysCache = new ForeignKeysCache(tableCache); } @Override public final TableCache getTableCache() { return tableCache; } @Override public final IndexCache getIndexCache() { return indexCache; } @Override public final PrimaryKeysCache getPrimaryKeysCache() { return primaryKeysCache; } @Override public final ForeignKeysCache getForeignKeysCache() { return foreignKeysCache; } @NotNull @Override public GenericDataSource getDataSource() { return dataSource; } @Override public boolean isPersisted() { return true; } @Override public Collection<GenericTable> getViews(DBRProgressMonitor monitor) throws DBException { Collection<GenericTable> tables = getTables(monitor); if (tables != null) { List<GenericTable> filtered = new ArrayList<>(); for (GenericTable table : tables) { if (table.isView()) { filtered.add(table); } } return filtered; } return null; } @Override public Collection<GenericTable> getPhysicalTables(DBRProgressMonitor monitor) throws DBException { Collection<GenericTable> tables = getTables(monitor); if (tables != null) { List<GenericTable> filtered = new ArrayList<>(); for (GenericTable table : tables) { if (!table.isView()) { filtered.add(table); } } return filtered; } return null; } @Override public Collection<GenericTable> getTables(DBRProgressMonitor monitor) throws DBException { return tableCache.getAllObjects(monitor, this); } @Override public GenericTable getTable(DBRProgressMonitor monitor, String name) throws DBException { return tableCache.getObject(monitor, this, name); } @Override public Collection<GenericTableIndex> getIndexes(DBRProgressMonitor monitor) throws DBException { cacheIndexes(monitor, true); return indexCache.getObjects(monitor, this, null); } private void cacheIndexes(DBRProgressMonitor monitor, boolean readFromTables) throws DBException { // Cache indexes (read all tables, all columns and all indexes in this container) // This doesn't work for generic datasource because metadata facilities // allows index query only by certain table name //cacheIndexes(monitor, null); synchronized (indexCache) { if (!indexCache.isFullyCached()) { try { // Try to load all indexes with one query Collection<GenericTableIndex> indexes = indexCache.getObjects(monitor, this, null); if (CommonUtils.isEmpty(indexes)) { // Nothing was read, Maybe driver doesn't support mass indexes reading indexCache.clearCache(); } } catch (Exception e) { log.debug(e); } // Failed if (!indexCache.isFullyCached() && readFromTables) { // Load indexes for all tables and return copy of them Collection<GenericTable> tables = getTables(monitor); monitor.beginTask("Cache indexes from tables", tables.size()); try { List<GenericTableIndex> tmpIndexMap = new ArrayList<>(); for (GenericTable table : tables) { if (monitor.isCanceled()) { return; } monitor.subTask("Read indexes for '" + table.getFullyQualifiedName(DBPEvaluationContext.DDL) + "'"); Collection<GenericTableIndex> tableIndexes = table.getIndexes(monitor); tmpIndexMap.addAll(tableIndexes); monitor.worked(1); } indexCache.setCache(tmpIndexMap); } finally { monitor.done(); } } } } } @Override public void cacheStructure(@NotNull DBRProgressMonitor monitor, int scope) throws DBException { // Cache tables if ((scope & STRUCT_ENTITIES) != 0) { monitor.subTask("Cache tables"); tableCache.getAllObjects(monitor, this); } // Cache attributes if ((scope & STRUCT_ATTRIBUTES) != 0 && dataSource.supportsStructCache()) { // Try to cache columns // Cannot be sure that all jdbc drivers support reading of all catalog columns // So error here is not fatal try { monitor.subTask("Cache tables' columns"); tableCache.loadChildren(monitor, this, null); } catch (Exception e) { log.debug(e); } } // Cache associations if ((scope & STRUCT_ASSOCIATIONS) != 0 && dataSource.supportsStructCache()) { // Try to read all PKs // Try to read all FKs try { monitor.subTask("Cache primary keys"); Collection<GenericPrimaryKey> objects = primaryKeysCache.getObjects(monitor, this, null); if (CommonUtils.isEmpty(objects)) { // Nothing was read, Maybe driver doesn't support mass keys reading primaryKeysCache.clearCache(); } } catch (Exception e) { // Failed - seems to be unsupported feature log.debug(e); } if (dataSource.getInfo().supportsIndexes()) { // Try to read all indexes monitor.subTask("Cache indexes"); cacheIndexes(monitor, false); } if (dataSource.getInfo().supportsReferentialIntegrity()) { // Try to read all FKs try { monitor.subTask("Cache foreign keys"); Collection<GenericTableForeignKey> foreignKeys = foreignKeysCache.getObjects(monitor, this, null); if (CommonUtils.isEmpty(foreignKeys)) { // Nothing was read, Maybe driver doesn't support mass keys reading foreignKeysCache.clearCache(); } } catch (Exception e) { // Failed - seems to be unsupported feature log.debug(e); } } } } @Override public synchronized Collection<GenericPackage> getPackages(DBRProgressMonitor monitor) throws DBException { if (procedures == null) { loadProcedures(monitor); } return packages == null ? null : packages; } public GenericPackage getPackage(DBRProgressMonitor monitor, String name) throws DBException { return DBUtils.findObject(getPackages(monitor), name); } @Override public synchronized Collection<GenericProcedure> getProcedures(DBRProgressMonitor monitor) throws DBException { if (procedures == null) { loadProcedures(monitor); } return procedures; } @Override public GenericProcedure getProcedure(DBRProgressMonitor monitor, String uniqueName) throws DBException { for (GenericProcedure procedure : CommonUtils.safeCollection(getProcedures(monitor))) { if (uniqueName.equals(procedure.getUniqueName())) { return procedure; } } return null; } @Override public Collection<GenericProcedure> getProcedures(DBRProgressMonitor monitor, String name) throws DBException { return DBUtils.findObjects(getProcedures(monitor), name); } @Override public Collection<GenericSequence> getSequences(DBRProgressMonitor monitor) throws DBException { if (sequences == null) { loadSequences(monitor); } return sequences; } @Override public Collection<? extends GenericTrigger> getTriggers(DBRProgressMonitor monitor) throws DBException { if (triggers == null) { loadTriggers(monitor); } return triggers; } @Override public Collection<? extends DBSObject> getChildren(@NotNull DBRProgressMonitor monitor) throws DBException { return getTables(monitor); } @Override public DBSObject getChild(@NotNull DBRProgressMonitor monitor, @NotNull String childName) throws DBException { return getTable(monitor, childName); } @Override public synchronized DBSObject refreshObject(@NotNull DBRProgressMonitor monitor) throws DBException { this.tableCache.clearCache(); this.indexCache.clearCache(); this.primaryKeysCache.clearCache(); this.foreignKeysCache.clearCache(); this.packages = null; this.procedures = null; this.sequences = null; return this; } public String toString() { return getName() == null ? "<NONE>" : getName(); } private synchronized void loadProcedures(DBRProgressMonitor monitor) throws DBException { dataSource.getMetaModel().loadProcedures(monitor, this); // Order procedures if (procedures != null) { DBUtils.orderObjects(procedures); } if (packages != null) { for (GenericPackage pack : packages) { pack.orderProcedures(); } } } public void addProcedure(GenericProcedure procedure) { if (procedures == null) { procedures = new ArrayList<>(); } procedures.add(procedure); } public boolean hasProcedure(String name) { if (procedures != null) { for (GenericProcedure proc : procedures) { if (proc.getName().equals(name)) { return true; } } } return false; } public void addPackage(GenericPackage procedurePackage) { if (packages == null) { packages = new ArrayList<>(); } packages.add(procedurePackage); } private synchronized void loadSequences(DBRProgressMonitor monitor) throws DBException { sequences = dataSource.getMetaModel().loadSequences(monitor, this); // Order procedures if (sequences == null) { sequences = new ArrayList<>(); } else { DBUtils.orderObjects(sequences); } } private synchronized void loadTriggers(DBRProgressMonitor monitor) throws DBException { triggers = dataSource.getMetaModel().loadTriggers(monitor, this, null); // Order procedures if (triggers == null) { triggers = new ArrayList<>(); } else { DBUtils.orderObjects(triggers); } } }