/* * 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.oracle.model; import org.jkiss.code.NotNull; import org.jkiss.code.Nullable; import org.jkiss.dbeaver.DBException; import org.jkiss.dbeaver.Log; import org.jkiss.dbeaver.model.DBUtils; import org.jkiss.dbeaver.model.exec.DBCException; import org.jkiss.dbeaver.model.exec.DBCSession; import org.jkiss.dbeaver.model.exec.jdbc.JDBCPreparedStatement; import org.jkiss.dbeaver.model.exec.jdbc.JDBCResultSet; import org.jkiss.dbeaver.model.exec.jdbc.JDBCSession; import org.jkiss.dbeaver.model.exec.jdbc.JDBCStatement; import org.jkiss.dbeaver.model.impl.AbstractExecutionSource; import org.jkiss.dbeaver.model.impl.jdbc.JDBCUtils; import org.jkiss.dbeaver.model.impl.jdbc.cache.JDBCStructCache; import org.jkiss.dbeaver.model.meta.*; import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor; import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor; import org.jkiss.dbeaver.model.struct.DBSObject; import org.jkiss.dbeaver.model.struct.DBSObjectLazy; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; /** * Oracle physical table */ public abstract class OracleTablePhysical extends OracleTableBase implements DBSObjectLazy<OracleDataSource> { private static final Log log = Log.getLog(OracleTablePhysical.class); public static final String CAT_STATISTICS = "Statistics"; //private boolean valid; private long rowCount; private Long realRowCount; private Object tablespace; private boolean partitioned; private PartitionInfo partitionInfo; private PartitionCache partitionCache; public OracleTablePhysical(OracleSchema schema, String name) { super(schema, name, false); } public OracleTablePhysical( OracleSchema schema, ResultSet dbResult) { super(schema, dbResult); this.rowCount = JDBCUtils.safeGetLong(dbResult, "NUM_ROWS"); //this.valid = "VALID".equals(JDBCUtils.safeGetString(dbResult, "STATUS")); this.tablespace = JDBCUtils.safeGetString(dbResult, "TABLESPACE_NAME"); this.partitioned = JDBCUtils.safeGetBoolean(dbResult, "PARTITIONED", "Y"); this.partitionCache = partitioned ? new PartitionCache() : null; } @Property(category = CAT_STATISTICS, viewable = true, order = 20) public long getRowCount() { return rowCount; } @Property(category = CAT_STATISTICS, viewable = false, expensive = true, order = 21) public synchronized Long getRealRowCount(DBRProgressMonitor monitor) { if (realRowCount != null) { return realRowCount; } if (!isPersisted()) { // Do not count rows for views return null; } // Query row count try (DBCSession session = DBUtils.openMetaSession(monitor, getDataSource(), "Read row count")) { realRowCount = countData(new AbstractExecutionSource(this, session.getExecutionContext(), this), session, null); } catch (DBException e) { log.debug("Can't fetch row count", e); } if (realRowCount == null) { realRowCount = -1L; } return realRowCount; } @Override public Object getLazyReference(Object propertyId) { return tablespace; } @Property(viewable = true, order = 22, editable = true, updatable = true, listProvider = TablespaceListProvider.class) @LazyProperty(cacheValidator = OracleTablespace.TablespaceReferenceValidator.class) public Object getTablespace(DBRProgressMonitor monitor) throws DBException { return OracleTablespace.resolveTablespaceReference(monitor, this, null); } public Object getTablespace() { return tablespace; } public void setTablespace(OracleTablespace tablespace) { this.tablespace = tablespace; } @Override @Association public Collection<OracleTableIndex> getIndexes(DBRProgressMonitor monitor) throws DBException { // Read indexes using cache return this.getContainer().indexCache.getObjects(monitor, getContainer(), this); } public OracleTableIndex getIndex(DBRProgressMonitor monitor, String name) throws DBException { return this.getContainer().indexCache.getObject(monitor, getContainer(), this, name); } @PropertyGroup @LazyProperty(cacheValidator = PartitionInfoValidator.class) public PartitionInfo getPartitionInfo(DBRProgressMonitor monitor) throws DBException { if (partitionInfo == null && partitioned) { try (final JDBCSession session = DBUtils.openMetaSession(monitor, getDataSource(), "Load partitioning info")) { try (JDBCPreparedStatement dbStat = session.prepareStatement("SELECT * FROM ALL_PART_TABLES WHERE OWNER=? AND TABLE_NAME=?")) { dbStat.setString(1, getContainer().getName()); dbStat.setString(2, getName()); try (JDBCResultSet dbResult = dbStat.executeQuery()) { if (dbResult.next()) { partitionInfo = new PartitionInfo(monitor, this.getDataSource(), dbResult); } } } } catch (SQLException e) { throw new DBException(e, getDataSource()); } } return partitionInfo; } @Association public Collection<OracleTablePartition> getPartitions(DBRProgressMonitor monitor) throws DBException { if (partitionCache == null) { return null; } else { this.partitionCache.getAllObjects(monitor, this); this.partitionCache.loadChildren(monitor, this, null); return this.partitionCache.getAllObjects(monitor, this); } } @Override public DBSObject refreshObject(@NotNull DBRProgressMonitor monitor) throws DBException { this.getContainer().indexCache.clearObjectCache(this); return super.refreshObject(monitor); } @Override public void refreshObjectState(@NotNull DBRProgressMonitor monitor) throws DBCException { this.valid = OracleUtils.getObjectStatus(monitor, this, OracleObjectType.TABLE); } private static class PartitionCache extends JDBCStructCache<OracleTablePhysical, OracleTablePartition, OracleTablePartition> { protected PartitionCache() { super("PARTITION_NAME"); } @Override protected JDBCStatement prepareObjectsStatement(@NotNull JDBCSession session, @NotNull OracleTablePhysical table) throws SQLException { final JDBCPreparedStatement dbStat = session.prepareStatement( "SELECT * FROM SYS.ALL_TAB_PARTITIONS " + "WHERE TABLE_OWNER=? AND TABLE_NAME=? " + "ORDER BY PARTITION_POSITION"); dbStat.setString(1, table.getContainer().getName()); dbStat.setString(2, table.getName()); return dbStat; } @Override protected OracleTablePartition fetchObject(@NotNull JDBCSession session, @NotNull OracleTablePhysical table, @NotNull JDBCResultSet resultSet) throws SQLException, DBException { return new OracleTablePartition(table, false, resultSet); } @Override protected JDBCStatement prepareChildrenStatement(@NotNull JDBCSession session, @NotNull OracleTablePhysical table, @Nullable OracleTablePartition forObject) throws SQLException { final JDBCPreparedStatement dbStat = session.prepareStatement( "SELECT * FROM SYS.ALL_TAB_SUBPARTITIONS " + "WHERE TABLE_OWNER=? AND TABLE_NAME=? " + (forObject == null ? "" : "AND PARTITION_NAME=?") + "ORDER BY SUBPARTITION_POSITION"); dbStat.setString(1, table.getContainer().getName()); dbStat.setString(2, table.getName()); if (forObject != null) { dbStat.setString(2, forObject.getName()); } return dbStat; } @Override protected OracleTablePartition fetchChild(@NotNull JDBCSession session, @NotNull OracleTablePhysical table, @NotNull OracleTablePartition parent, @NotNull JDBCResultSet dbResult) throws SQLException, DBException { return new OracleTablePartition(table, true, dbResult); } } public static class PartitionInfo extends OraclePartitionBase.PartitionInfoBase { public PartitionInfo(DBRProgressMonitor monitor, OracleDataSource dataSource, ResultSet dbResult) throws DBException { super(monitor, dataSource, dbResult); } } public static class PartitionInfoValidator implements IPropertyCacheValidator<OracleTablePhysical> { @Override public boolean isPropertyCached(OracleTablePhysical object, Object propertyId) { return object.partitioned && object.partitionInfo != null; } } public static class TablespaceListProvider implements IPropertyValueListProvider<OracleTablePhysical> { @Override public boolean allowCustomValue() { return false; } @Override public Object[] getPossibleValues(OracleTablePhysical object) { final List<OracleTablespace> tablespaces = new ArrayList<>(); try { for (OracleTablespace ts : object.getDataSource().getTablespaces(new VoidProgressMonitor())) { tablespaces.add(ts); } } catch (DBException e) { log.error(e); } Collections.sort(tablespaces, DBUtils.<OracleTablespace>nameComparator()); return tablespaces.toArray(new OracleTablespace[tablespaces.size()]); } } }