/* This file is part of VoltDB.
* Copyright (C) 2008-2010 VoltDB L.L.C.
*
* VoltDB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* VoltDB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with VoltDB. If not, see <http://www.gnu.org/licenses/>.
*/
package org.voltdb;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.voltdb.catalog.Catalog;
import org.voltdb.catalog.CatalogMap;
import org.voltdb.catalog.Cluster;
import org.voltdb.catalog.Database;
import org.voltdb.catalog.Host;
import org.voltdb.catalog.Partition;
import org.voltdb.catalog.PlanFragment;
import org.voltdb.catalog.Procedure;
import org.voltdb.catalog.Site;
import org.voltdb.catalog.Statement;
import org.voltdb.catalog.Table;
import org.voltdb.utils.JarClassLoader;
import edu.brown.catalog.CatalogUtil;
import edu.brown.mappings.ParameterMappingsSet;
import edu.brown.mappings.ParametersUtil;
import edu.brown.utils.PartitionSet;
public class CatalogContext {
// THE CATALOG!
public final Catalog catalog;
// PUBLIC IMMUTABLE CACHED INFORMATION
public final Cluster cluster;
public final Database database;
public final CatalogMap<Host> hosts;
public final CatalogMap<Site> sites;
// ParameterMappingsSet
public final ParameterMappingsSet paramMappings;
/** The total number of unique Hosts in the cluster */
public final int numberOfHosts;
/** The total number of unique Sites in the cluster */
public final int numberOfSites;
/** The total number of unique Partitions in the cluster */
public final int numberOfPartitions;
/** The total number of unique tables in the database */
public final int numberOfTables;
// PRIVATE
public final File jarPath;
private final JarClassLoader catalogClassLoader;
// ------------------------------------------------------------
// PARTITIONS
// ------------------------------------------------------------
private final Partition partitions[];
private final PartitionSet partitionIdCollection = new PartitionSet();
private final Integer partitionIdArray[];
/** PartitionId -> Singleton set of that PartitionId */
private final PartitionSet partitionSingletons[];
/** PartitionId -> SiteId */
private final int partitionSiteXref[];
// ------------------------------------------------------------
// PROCEDURES
// ------------------------------------------------------------
public final CatalogMap<Procedure> procedures;
private final Collection<Procedure> regularProcedures = new ArrayList<Procedure>();
private final Collection<Procedure> sysProcedures = new ArrayList<Procedure>();
private final Collection<Procedure> mrProcedures = new ArrayList<Procedure>();
private final Procedure proceduresArray[];
// ------------------------------------------------------------
// STATEMENTS
// ------------------------------------------------------------
private final Map<Integer, Statement> stmtIdXref = new HashMap<Integer, Statement>();
// ------------------------------------------------------------
// TABLES
// ------------------------------------------------------------
private final Collection<Table> sysTables = new ArrayList<Table>();
private final Collection<Table> dataTables = new ArrayList<Table>();
private final Collection<Table> viewTables = new ArrayList<Table>();
private final Collection<Table> mapReduceTables = new ArrayList<Table>();
private final Collection<Table> replicatedTables = new ArrayList<Table>();
private final Collection<Table> evictableTables = new ArrayList<Table>();
// ------------------------------------------------------------
// PLANFRAGMENTS
// ------------------------------------------------------------
/**
* PlanFragmentId -> TableIds Read/Written
*/
private final Map<Long, int[]> fragmentReadTables = new HashMap<Long, int[]>();
private final Map<Long, int[]> fragmentWriteTables = new HashMap<Long, int[]>();
public CatalogContext(Catalog catalog) {
this(catalog, (File)null);
}
public CatalogContext(Catalog catalog, String pathToCatalogJar) {
this(catalog, new File(pathToCatalogJar));
}
public CatalogContext(Catalog catalog, File pathToCatalogJar) {
// check the heck out of the given params in this immutable class
assert(catalog != null);
if (catalog == null) {
throw new RuntimeException("Can't create CatalogContext with null catalog.");
}
this.jarPath = pathToCatalogJar;
this.catalog = catalog;
this.cluster = CatalogUtil.getCluster(this.catalog);
this.database = CatalogUtil.getDatabase(this.catalog);
this.hosts = this.cluster.getHosts();
this.sites = this.cluster.getSites();
if (this.jarPath != null) {
this.catalogClassLoader = new JarClassLoader(this.jarPath.getAbsolutePath());
this.paramMappings = ParametersUtil.getParameterMappingsSetFromJar(this.database, this.jarPath);
} else {
this.catalogClassLoader = null;
this.paramMappings = null;
}
// ------------------------------------------------------------
// PROCEDURES
// ------------------------------------------------------------
this.procedures = database.getProcedures();
this.proceduresArray = new Procedure[this.procedures.size()+1];
for (Procedure proc : this.procedures) {
this.proceduresArray[proc.getId()] = proc;
if (proc.getSystemproc()) {
this.sysProcedures.add(proc);
}
else if (proc.getMapreduce()) {
this.mrProcedures.add(proc);
}
else {
this.regularProcedures.add(proc);
}
} // FOR
// count nodes
this.numberOfHosts = cluster.getHosts().size();
// count exec sites
this.numberOfSites = cluster.getSites().size();
// ------------------------------------------------------------
// PARTITIONS
// ------------------------------------------------------------
this.numberOfPartitions = cluster.getNum_partitions();
this.partitions = new Partition[this.numberOfPartitions];
this.partitionIdArray = new Integer[this.numberOfPartitions];
this.partitionSingletons = new PartitionSet[this.numberOfPartitions];
this.partitionSiteXref = new int[this.numberOfPartitions];
for (Partition part : CatalogUtil.getAllPartitions(catalog)) {
int p = part.getId();
this.partitions[p] = part;
this.partitionIdArray[p] = Integer.valueOf(p);
this.partitionSingletons[p] = new PartitionSet(p);
this.partitionIdCollection.add(this.partitionIdArray[p]);
this.partitionSiteXref[part.getId()] = ((Site)part.getParent()).getId();
} // FOR
// ------------------------------------------------------------
// TABLES
// ------------------------------------------------------------
for (Table tbl : this.database.getTables()) {
// SYSTEM TABLE
if (tbl.getSystable()) {
this.sysTables.add(tbl);
}
// MAPREDUCE TABLE
else if (tbl.getMapreduce()) {
this.mapReduceTables.add(tbl);
}
// MATERIALIZED VIEW TABLE
else if (tbl.getMaterializer() != null) {
this.viewTables.add(tbl);
}
// REGULAR DATA TABLE
else {
this.dataTables.add(tbl);
if (tbl.getIsreplicated()) {
this.replicatedTables.add(tbl);
}
if (tbl.getEvictable() && !tbl.getBatchevicted()) {
this.evictableTables.add(tbl);
}
}
} // FOR
this.numberOfTables = database.getTables().size();
// PLANFRAGMENTS
this.initPlanFragments();
}
private void initPlanFragments() {
Set<PlanFragment> allFrags = new HashSet<PlanFragment>();
for (Procedure proc : database.getProcedures()) {
for (Statement stmt : proc.getStatements()) {
allFrags.clear();
allFrags.addAll(stmt.getFragments());
allFrags.addAll(stmt.getMs_fragments());
for (PlanFragment frag : allFrags) {
Collection<Table> tables = CatalogUtil.getReferencedTables(frag);
int tableIds[] = new int[tables.size()];
int i = 0;
for (Table tbl : tables) {
tableIds[i++] = tbl.getRelativeIndex();
} // FOR
if (frag.getReadonly()) {
this.fragmentReadTables.put(Long.valueOf(frag.getId()), tableIds);
} else {
this.fragmentWriteTables.put(Long.valueOf(frag.getId()), tableIds);
}
} // FOR (frag)
} // FOR (stmt)
} // FOR (proc)
}
public CatalogContext deepCopy() {
return new CatalogContext(catalog.deepCopy(), jarPath);
}
public CatalogContext update(String pathToNewJar, String diffCommands) {
Catalog newCatalog = catalog.deepCopy();
newCatalog.execute(diffCommands);
CatalogContext retval = new CatalogContext(newCatalog, pathToNewJar);
return retval;
}
/**
* Given a class name in the catalog jar, loads it from the jar, even if the
* jar is served from a url and isn't in the classpath.
*
* @param procedureClassName The name of the class to load.
* @return A java Class variable assocated with the class.
* @throws ClassNotFoundException if the class is not in the jar file.
*/
public Class<?> classForProcedure(String procedureClassName) throws ClassNotFoundException {
//System.out.println("Loading class " + procedureClassName);
// this is a safety mechanism to prevent catalog classes overriding voltdb stuff
if (procedureClassName.startsWith("org.voltdb."))
return Class.forName(procedureClassName);
// look in the catalog for the file
return catalogClassLoader.loadClass(procedureClassName);
}
// ------------------------------------------------------------
// SITES
// ------------------------------------------------------------
/**
* Return the unique Site catalog object for the given id
* @param catalog_item
* @return
*/
public Site getSiteById(int site_id) {
assert(site_id >= 0);
return (this.sites.get("id", site_id));
}
/**
* Return the site id for the given partition
* @param partition_id
* @return
*/
public int getSiteIdForPartitionId(int partition_id) {
return (this.partitionSiteXref[partition_id]);
}
/**
* Return the site for the given partition
* @param partition
* @return
*/
public Site getSiteForPartition(int partition) {
int siteId = this.partitionSiteXref[partition];
return (this.getSiteById(siteId));
}
// ------------------------------------------------------------
// PARTITIONS
// ------------------------------------------------------------
/**
* Return an array containing all the Partition handles in the cluster
*/
public Partition[] getAllPartitions() {
return (this.partitions);
}
/**
* Return the Partition catalog object for the given PartitionId
* @param id
* @return
*/
public Partition getPartitionById(int id) {
return (this.partitions[id]);
}
/**
* Return all the partition ids in this H-Store database cluster
*/
public PartitionSet getAllPartitionIds() {
return (this.partitionIdCollection);
}
/**
* Return all the partition ids in this H-Store database cluster
*/
public Integer[] getAllPartitionIdArray() {
return (this.partitionIdArray);
}
/**
* Return a PartitionSet that only contains the given partition id
* @param partition
*/
public PartitionSet getPartitionSetSingleton(int partition) {
return (this.partitionSingletons[partition]);
}
// ------------------------------------------------------------
// TABLES + COLUMNS
// ------------------------------------------------------------
/**
* Return the Table for the given name
* @param tableName
*/
public Table getTableByName(String tableName) {
return (this.database.getTables().getIgnoreCase(tableName));
}
/**
* Return the Table for the given id
* @param tableId
*/
public Table getTableById(int tableId) {
// HACK HACK HACK
assert(tableId-1 >= 0);
Table tables[] = this.database.getTables().values();
if (tableId-1 >= tables.length) {
String msg = "Invalid tableId '" + tableId + "'";
throw new IllegalArgumentException(msg);
}
return (tables[tableId-1]);
}
/**
* Return all of the internal system tables for the database
*/
public Collection<Table> getSysTables() {
return (sysTables);
}
/**
* Return all of the user-defined data tables for the database
*/
public Collection<Table> getDataTables() {
return (dataTables);
}
/**
* Return all of the materialized view tables for the database
*/
public Collection<Table> getViewTables() {
return (viewTables);
}
/**
* Return all of the MapReduce input data tables for the database
*/
public Collection<Table> getMapReduceTables() {
return (mapReduceTables);
}
/**
* Return all of the replicated tables for the database
*/
public Collection<Table> getReplicatedTables() {
return (replicatedTables);
}
/**
* Return all of the evictable tables for the database
*/
public Collection<Table> getEvictableTables() {
return (evictableTables);
}
// ------------------------------------------------------------
// PROCEDURES
// ------------------------------------------------------------
public Procedure getProcedureById(int procId) {
if (procId >= 0 && procId < this.proceduresArray.length) {
return (this.proceduresArray[procId]);
}
return (null);
}
/**
* Return all of the regular transactional Procedures in the catalog
*/
public Collection<Procedure> getRegularProcedures() {
return (this.regularProcedures);
}
/**
* Return all of the internal system Procedures in the catalog
*/
public Collection<Procedure> getSysProcedures() {
return (this.sysProcedures);
}
/**
* Return all of the MapReduce Procedures in the catalog
*/
public Collection<Procedure> getMapReduceProcedures() {
return (this.mrProcedures);
}
// ------------------------------------------------------------
// STATEMENTS
// ------------------------------------------------------------
public Statement getStatementById(int stmtId) {
// HACK: The first call will actually build the cache
if (this.stmtIdXref.isEmpty()) {
synchronized (this.stmtIdXref) {
if (this.stmtIdXref.isEmpty()) {
for (Procedure catalog_proc : this.procedures.values()) {
for (Statement catalog_stmt : catalog_proc.getStatements().values()) {
this.stmtIdXref.put(catalog_stmt.getId(), catalog_stmt);
} // FOR (stmt)
} // FOR (proc)
}
} // SYNCH
}
return (this.stmtIdXref.get(stmtId));
}
// ------------------------------------------------------------
// PLANFRAGMENTS
// ------------------------------------------------------------
/**
* Return the tableIds that are read by this PlanFragment
* @param planFragmentId
* @return
*/
public int[] getReadTableIds(Long planFragmentId) {
return (this.fragmentReadTables.get(planFragmentId));
}
/**
* Return the tableIds that are written by this PlanFragment
* @param planFragmentId
* @return
*/
public int[] getWriteTableIds(Long planFragmentId) {
return (this.fragmentWriteTables.get(planFragmentId));
}
}