package com.tesora.dve.common.catalog;
/*
* #%L
* Tesora Inc.
* Database Virtualization Engine
* %%
* Copyright (C) 2011 - 2014 Tesora Inc.
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License, version 3,
* as published by the Free Software Foundation.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
* #L%
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.LockModeType;
import javax.persistence.Persistence;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.sql.DataSource;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Statistics;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import com.mchange.v2.c3p0.C3P0Registry;
import com.tesora.dve.common.DBHelper;
import com.tesora.dve.common.PEUrl;
import com.tesora.dve.distribution.DistributionRange;
import com.tesora.dve.distribution.RangeTableRelationship;
import com.tesora.dve.distribution.RangeTableRelationship.RangeTableRelationshipCacheLookup;
import com.tesora.dve.exceptions.PECodingException;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.exceptions.PENotFoundException;
import com.tesora.dve.groupmanager.GroupManager;
import com.tesora.dve.sql.util.Functional;
public class CatalogDAO {
private static Logger logger = Logger.getLogger(CatalogDAO.class);
AtomicReference<EntityManager> em = new AtomicReference<EntityManager>();
int transactionDepth = 0;
private CatalogDAO(EntityManager em) {
this.em.set(em);
}
public void close() {
EntityManager theEm = em.getAndSet(null);
if (theEm != null && theEm.isOpen())
theEm.close();
}
@Override
public String toString() {
return "CatalogDAO@" + System.identityHashCode(this);
}
public void begin() {
if (transactionDepth++ == 0) {
em.get().getTransaction().begin();
// System.out.println("@" + System.identityHashCode(this) +
// " beginning new txn");
}
}
public void nonNestedBegin() {
if (transactionDepth > 0)
throw new PECodingException("Attempt to start non-nested transaction inside transaction scope");
++transactionDepth;
em.get().getTransaction().begin();
}
public Query createQuery(String queryString) {
return em.get().createQuery(queryString);
}
public void commit() {
if (transactionDepth == 1) {
em.get().getTransaction().commit();
}
if (transactionDepth > 0)
--transactionDepth;
}
public void rollbackNoException() {
if (transactionDepth > 0)
em.get().getTransaction().rollback();
transactionDepth = 0;
}
public void rollback(Throwable t) {
try {
if (em.get().getTransaction().isActive())
rollbackNoException();
} catch (Throwable rollbackT) {
logger.warn("Exception during rollback", rollbackT);
throw new PersistenceException("Exception during rollback (check log for rollback exception)", t);
}
}
public void retryableRollback(Throwable t) {
try {
if (em.get().getTransaction().isActive())
rollbackNoException();
} catch (Throwable rollbackT) {
logger.warn("Exception during rollback", rollbackT);
logger.warn("Original exception", t);
}
}
public void cleanupRollback() {
if (em.get().getTransaction().isActive())
em.get().getTransaction().rollback();
transactionDepth = 0;
}
public void persistToCatalog(Object o) {
em.get().persist(o);
}
@SuppressWarnings("unchecked")
public <T extends CatalogEntity> T findByKey(Class<? extends CatalogEntity> c, int id) {
return (T) em.get().find(c, id);
}
@SuppressWarnings("unchecked")
public <T extends CatalogEntity> T findByName(Class<? extends CatalogEntity> tClass, String entityName,
boolean except) throws PENotFoundException {
return (T) CatalogDAOFactory.INSTANCE.getLookupCache(tClass, "name").findByValue(this, entityName, except);
}
@SuppressWarnings("unchecked")
private <T> List<T> findAllByClass(Class<T> entityClass) {
Query q = em.get().createQuery("from " + entityClass.getSimpleName());
return (List<T>) q.getResultList();
}
@SuppressWarnings("unchecked")
public List<CatalogEntity> queryCatalogEntity(String queryStr) {
return queryCatalogEntity(queryStr, Collections.EMPTY_MAP);
}
@SuppressWarnings("rawtypes")
public List nativeQuery(String nativeQueryString, Map<String, Object> params) {
Query q = em.get().createNativeQuery(nativeQueryString);
for (Map.Entry<String, Object> me : params.entrySet())
q.setParameter(me.getKey(), me.getValue());
return q.getResultList();
}
@SuppressWarnings("rawtypes")
public List nativeQuery(String nativeQueryString, Map<String, Object> params, Class<?> targ) {
Query q = em.get().createNativeQuery(nativeQueryString, targ);
for (Map.Entry<String, Object> me : params.entrySet())
q.setParameter(me.getKey(), me.getValue());
return q.getResultList();
}
@SuppressWarnings("unchecked")
public List<CatalogEntity> queryCatalogEntity(String queryStr, Map<String, Object> params) {
Query query = em.get().createQuery(queryStr);
for (Map.Entry<String, Object> me : params.entrySet())
query.setParameter(me.getKey(), me.getValue());
return (List<CatalogEntity>) query.getResultList();
}
public List<User> findAllUsers() {
return findAllByClass(User.class);
}
public List<DistributionModel> findAllDistributionModels() {
return findAllByClass(DistributionModel.class);
}
public List<UserDatabase> findAllUserDatabases() {
return findAllByClass(UserDatabase.class);
}
public List<Project> findAllProjects() {
return findAllByClass(Project.class);
}
public List<Provider> findAllProviders() {
return findAllByClass(Provider.class);
}
public List<DynamicPolicy> findAllDynamicPolicies() {
return findAllByClass(DynamicPolicy.class);
}
public List<ExternalService> findAllExternalServices() {
return findAllByClass(ExternalService.class);
}
public List<CharacterSets> findAllCharacterSets() {
return findAllByClass(CharacterSets.class);
}
public List<Collations> findAllCollations() {
return findAllByClass(Collations.class);
}
@SuppressWarnings("unchecked")
public List<UserTable> findAllTablesInPersistentGroup(PersistentGroup sg) {
Query query = em.get().createQuery("from UserTable ut where ut.persistentGroup = :storageGroup");
query.setParameter("storageGroup", sg);
return (List<UserTable>) query.getResultList();
}
@SuppressWarnings("unchecked")
public PersistentGroup findBalancedPersistentGroup(String prefix) {
StringBuilder builder = new StringBuilder(
"select pg.persistent_group_id, count(ud.user_database_id) as usecount from persistent_group pg left outer join user_database ud on pg.persistent_group_id = ud.default_group_id where pg.name not in ('InformationSchemaGroup','SystemGroup') ");
if (StringUtils.isNotBlank(prefix))
builder.append("and pg.name like '" + prefix + "%'");
builder.append(" group by pg.persistent_group_id order by usecount asc, pg.persistent_group_id limit 1");
Query query = em.get().createNativeQuery(builder.toString());
List<Object[]> out = query.getResultList();
if (out.isEmpty())
return null;
if (out.size() != 1)
throw new IllegalStateException("Failed to find persistent group");
Object[] first = out.get(0);
return (PersistentGroup) findByKey(PersistentGroup.class, (Integer) first[0]);
}
@SuppressWarnings("unchecked")
public List<UserDatabase> findAllMTDatabases() {
Query query = em.get().createQuery("from UserDatabase ud where ud.multitenant_mode != 'off'");
return (List<UserDatabase>) query.getResultList();
}
public DistributionModel findDistributionModel(String name) throws PEException {
return findByName(DistributionModel.class, name, true);
}
public Map<String, DistributionModel> getDistributionModelMap() {
Map<String, DistributionModel> modelMap = new HashMap<String, DistributionModel>();
for (DistributionModel d : findAllByClass(DistributionModel.class))
modelMap.put(d.getName(), d);
return modelMap;
}
public PersistentSite createPersistentSite(String name, String url, String user, String password)
throws PEException {
SiteInstance si = new SiteInstance(name, url, user, password);
PersistentSite db = new PersistentSite(name, si);
em.get().persist(db);
em.get().persist(si);
return db;
}
public PersistentSite createPersistentSite(String siteName, String haType, SiteInstance master,
SiteInstance[] replicants) throws PEException {
PersistentSite theSite = new PersistentSite(siteName, master);
theSite.setHaType(haType);
em.get().persist(theSite);
theSite.addAll(replicants);
return theSite;
}
public PersistentGroup createPersistentGroup(String name) {
PersistentGroup sc = new PersistentGroup(name);
em.get().persist(sc);
return sc;
}
public UserTable createUserTable(UserDatabase c, String name,
DistributionModel dist, PersistentGroup sc, String engine, String tableType) {
UserTable ut = new UserTable(name, dist, c, TableState.SHARED, engine, tableType);
// dist.addUserTable(ut);
ut.setPersistentGroup(sc);
c.addUserTable(ut);
em.get().persist(ut);
return ut;
}
public User createUser(String name, String password, String accessSpec) {
User u = new User(name, password, accessSpec);
em.get().persist(u);
return u;
}
public User createUser(String name, String password, String accessSpec, boolean adminUser) {
User u = new User(name, password, accessSpec, adminUser);
em.get().persist(u);
return u;
}
public UserColumn createUserColumn(UserTable ut, String name, int dataType,
String nativeTypeName, int size) {
// TODO figure out best way to inject other column values
// into this...
UserColumn uc = new UserColumn(ut, name, dataType, nativeTypeName, size);
uc.setAutoGenerated(false);
uc.setNullable(true);
uc.setHasDefault(false);
return uc;
}
public UserDatabase createDatabase(String name, PersistentGroup sg, String charSet, String collation) {
UserDatabase c = new UserDatabase(name, sg, null, TemplateMode.OPTIONAL, MultitenantMode.OFF, FKMode.STRICT, charSet, collation);
em.get().persist(c);
return c;
}
public Project createProject(String name) {
Project p = new Project(name);
em.get().persist(p);
return p;
}
public Engines createEngines(String engine, String support, String comment, String transactions, String xa,
String savepoints) {
Engines engines = new Engines(engine, support, comment, transactions, xa, savepoints);
em.get().persist(engines);
return engines;
}
public Provider createProvider(String name, String plugin) {
Provider prov = new Provider(name, plugin);
em.get().persist(prov);
return prov;
}
public Provider createProvider(String name, String plugin, String config) {
Provider prov = new Provider(name, plugin, StringEscapeUtils.escapeSql(config));
em.get().persist(prov);
return prov;
}
public ExternalService createExternalService(String name, String plugin, String connectUser, boolean usesDataStore) throws PEException {
return createExternalService(name, plugin, connectUser, usesDataStore, null);
}
public ExternalService createExternalService(String name, String plugin, String connectUser, boolean usesDataStore,
String config) throws PEException {
ExternalService extServ = new ExternalService(name, plugin, connectUser, usesDataStore);
if (config != null)
extServ.setConfig(config);
em.get().persist(extServ);
return extServ;
}
// -----------------------------------------------------------------
// FIND Methods
//
public List<PersistentGroup> findAllPersistentGroups() {
return findAllByClass(PersistentGroup.class);
}
public List<UserTable> findAllUserTables() {
return findAllByClass(UserTable.class);
}
public List<UserColumn> findAllUserColumns() {
return findAllByClass(UserColumn.class);
}
public List<PersistentSite> findAllPersistentSites() {
return findAllByClass(PersistentSite.class);
}
public PersistentGroup findDefaultPersistentGroup() throws PEException {
return findByName(PersistentGroup.class, "Default", true);
}
public Project findDefaultProject() throws PEException {
return findProject(Project.DEFAULT, true);
}
public Project findProject(String projectName) throws PEException {
return findProject(projectName, true);
}
public Project findProject(String projectName, boolean except) throws PEException {
return (Project) CatalogDAOFactory.INSTANCE.getLookupCache(Project.class, "name").findByValue(this,
projectName, except);
}
public VariableConfig findVariableConfig(String variableName) throws PENotFoundException {
return findVariableConfig(variableName, true);
}
public VariableConfig findVariableConfig(String variableName, boolean except) throws PENotFoundException {
return (VariableConfig) CatalogDAOFactory.INSTANCE.getLookupCache(VariableConfig.class, "name").findByValue(this,
variableName, except);
}
public List<VariableConfig> findAllVariableConfigs() {
return findAllByClass(VariableConfig.class);
}
public UserDatabase findDatabase(String databaseName) throws PEException {
return findDatabase(databaseName, true);
}
public UserDatabase findDatabase(String databaseName, boolean except) throws PEException {
return findByName(UserDatabase.class, databaseName, except);
}
public PersistentGroup findPersistentGroup(String groupName) throws PEException {
return findPersistentGroup(groupName, true);
}
public PersistentGroup findPersistentGroup(String groupName, boolean except) throws PEException {
return findByName(PersistentGroup.class, groupName, except);
}
public PersistentSite findPersistentSite(String siteName) throws PEException {
return findPersistentSite(siteName, true);
}
public PersistentGroup buildAllSitesGroup() throws PEException {
List<PersistentSite> sites = findAllPersistentSites();
final LinkedHashMap<String,PersistentSite> uniqueURLS = new LinkedHashMap<String,PersistentSite>();
for(PersistentSite ps : sites) {
String key = ps.getMasterUrl();
PersistentSite already = uniqueURLS.get(key);
if (already == null)
uniqueURLS.put(key,ps);
}
return new PersistentGroup(uniqueURLS.values());
}
@SuppressWarnings("unchecked")
public static <T> T onlyOne(List<T> results, String what, String searchedOn, boolean except)
throws PENotFoundException {
if (results.size() > 1 || (results.size() == 0 && except)) {
throw new PENotFoundException("Expected exactly one " + what + " for name " + searchedOn);
}
if (results.size() == 0)
return null;
return (T) results.toArray()[0];
}
@SuppressWarnings("unchecked")
public static <T> T onlyOne(List<? extends NamedCatalogEntity> results, String what, String dbid, String name,
boolean except, boolean quoted) throws PENotFoundException {
String searchedOn = dbid + ((name != null) ? ("/" + name) : "");
if (results.size() == 0 && except) {
throw new PENotFoundException("Expected exactly one " + what + " for name " + searchedOn);
}
if (results.size() == 0)
return null;
T ce = null;
for (int i = 0; i < results.size(); i++) {
T entity = (T) results.get(i);
ce = findExactNameMatch(entity, name);
if (ce != null) {
break;
}
}
if (quoted) {
if (except && ce == null) {
throw new PENotFoundException("Expected exactly one " + what + " for name " + searchedOn);
}
return ce;
} else {
// not quoted so if exact match then return it
if (ce != null) {
return ce;
}
// otherwise return any matching entity
return (T) results.get(0);
}
}
static private <T> T findExactNameMatch(T ce, String searchedOn) {
T ret = null;
// must be exact match
if (StringUtils.equals(searchedOn, ((NamedCatalogEntity) ce).getName())) {
ret = ce;
}
return ret;
}
public PersistentSite findPersistentSite(String siteName, boolean except) throws PEException {
return findByName(PersistentSite.class, siteName, except);
}
public <T> T merge(T o) {
return em.get().merge(o);
}
public <T> void detach(T o) {
em.get().detach(o);
}
public boolean isDetached(Object o) {
return !em.get().contains(o);
}
public <T> void remove(T o) {
em.get().remove(o);
CatalogDAOFactory.INSTANCE.clearLookupCache(o.getClass());
}
public <T> void refresh(T o) {
try {
em.get().refresh(o);
} catch (IllegalArgumentException iae) {
throw iae;
}
}
public <T> void refreshForLock(T o) {
em.get().refresh(o, LockModeType.PESSIMISTIC_WRITE);
}
public DistributionRange findRangeForTable(UserTable userTable) throws PEException {
return findRangeTableRelationship(userTable, true).getRange();
}
public DistributionRange findRangeForTableByName(String qualifiedTableName, int tableId) throws PEException {
return findRangeTableRelationshipByTableId(qualifiedTableName, tableId, true).getRange();
}
@SuppressWarnings("unchecked")
public DistributionRange findRangeByName(String name, String groupName, boolean except) throws PEException {
Query q = em.get().createQuery("from DistributionRange dr where dr.name = :name and dr.storageGroup.name = :groupName");
q.setParameter("name", name);
q.setParameter("groupName", groupName);
List<DistributionRange> results = q.getResultList();
return onlyOne(results, "DistributionRange", name + "/" + groupName, except);
}
@SuppressWarnings("unchecked")
public List<DistributionRange> findRangeByName(String name) throws PEException {
Query q = em.get().createQuery("from DistributionRange dr where dr.name = :name");
q.setParameter("name", name);
return q.getResultList();
}
@SuppressWarnings("unchecked")
public List<DistributionRange> findRangesOnGroup(String groupName) throws PEException {
Query q = em.get().createQuery("from DistributionRange dr where dr.storageGroup.name = :groupName");
q.setParameter("groupName", groupName);
return q.getResultList();
}
// existence test, mostly
public RangeTableRelationship findRangeTableRelationship(UserTable ut, boolean except) throws PEException {
return (RangeTableRelationship) CatalogDAOFactory.INSTANCE
.getLookupCache(RangeTableRelationship.class, "table").findByValue(this, ut.getQualifiedName(), ut,
except);
}
public RangeTableRelationship findRangeTableRelationshipByTableId(String tableQualifiedName, int tableId,
boolean except) throws PEException {
return (RangeTableRelationship) CatalogDAOFactory.INSTANCE.getLookupCache(RangeTableRelationship.class,
"table.id").findByValue(this, tableQualifiedName, tableId, except);
}
public Provider findProvider(String name) throws PEException {
return findProvider(name, true);
}
public Provider findProvider(String name, boolean except) throws PEException {
return findByName(Provider.class, name, except);
}
public DynamicPolicy findDynamicPolicy(String name) throws PEException {
return findDynamicPolicy(name, true);
}
public DynamicPolicy findDynamicPolicy(String name, boolean except) throws PEException {
return findByName(DynamicPolicy.class, name, except);
}
public ExternalService findExternalService(String name) throws PEException {
return findExternalService(name, true);
}
public ExternalService findExternalService(String name, boolean except) throws PEException {
return findByName(ExternalService.class, name, except);
}
@SuppressWarnings("unchecked")
public List<User> findUsers(String name, String accessSpec) throws PEException {
Query query = null;
if (accessSpec == null)
query = em.get().createQuery("from User u where u.name = :name");
else {
query = em.get().createQuery("from User u where u.name = :name and u.accessSpec = :accessSpec");
query.setParameter("accessSpec", accessSpec);
}
query.setParameter("name", name);
return (List<User>) query.getResultList();
}
public User findUser(String name, boolean except) throws PEException {
List<User> candidates = findUsers(name, null);
if (candidates.isEmpty()) {
if (except)
throw new PENotFoundException("User " + name + " not found in catalog");
return null;
}
User catalogUser = null;
catalogUser = candidates.get(0);
if (candidates.size() > 1)
logger.debug("More than one user found, choosing '" + catalogUser.getName() + "'@'"
+ catalogUser.getAccessSpec() + "'");
return catalogUser;
}
public User findUser(String name) throws PEException {
return findUser(name, true);
}
public List<Tenant> findAllTenants() throws PEException {
return findAllByClass(Tenant.class);
}
public Tenant findTenant(String extID) throws PEException {
return findTenant(extID, true);
}
public Tenant findTenant(String extID, boolean except) throws PEException {
return (Tenant) CatalogDAOFactory.INSTANCE.getLookupCache(Tenant.class, "externalID").findByValue(this, extID,
except);
}
public List<TableVisibility> findAllTenantScopes() {
return findAllByClass(TableVisibility.class);
}
public List<TableVisibility> findTenantScopesForTable(UserTable ut) {
Query query = em.get().createQuery("from TableVisibility tv where tv.table = :ut");
query.setParameter("ut", ut);
@SuppressWarnings("unchecked")
List<TableVisibility> res = query.getResultList();
return res;
}
public TableVisibility findScopeForTable(UserTable ut, Tenant t, boolean except) throws PEException {
Query query = em.get().createQuery("from TableVisibility tv where tv.table = :ut and tv.ofTenant = :t");
query.setParameter("ut", ut);
query.setParameter("t", t);
@SuppressWarnings("unchecked")
List<TableVisibility> res = query.getResultList();
return onlyOne(res, "TenantVisibility", ut.getName() + "/" + t.getExternalTenantId(), except);
}
public TableVisibility findScopeForTable(int tableID, int tenantID, boolean except) throws PEException {
Query query = em.get().createQuery(
"from TableVisibility tv where tv.table.id = :tableID and tv.ofTenant.id = :tenantID");
query.setParameter("tableID", tableID);
query.setParameter("tenantID", tenantID);
@SuppressWarnings("unchecked")
List<TableVisibility> res = query.getResultList();
return onlyOne(res, "TenantVisibility", tableID + "/" + tenantID, except);
}
// only used in adaptive mode - thus always has localName
public TableVisibility findScope(int tenantID, String localName) throws PEException {
Query query = em.get().createQuery(
"from TableVisibility tv where tv.ofTenant.id = :tenantID and tv.localName = :localName");
query.setParameter("tenantID", tenantID);
query.setParameter("localName", localName);
@SuppressWarnings("unchecked")
List<TableVisibility> res = query.getResultList();
return onlyOne(res, "TenantVisibility", localName + "/" + tenantID, true);
}
@SuppressWarnings("unchecked")
public List<Priviledge> findPriviledgesOnDatabase(UserDatabase udb) throws PEException {
Query query = em.get().createQuery("from Priviledge p where p.database = :udb");
query.setParameter("udb", udb);
return (List<Priviledge>) query.getResultList();
}
@SuppressWarnings("unchecked")
public List<Tenant> findTenantsOnDatabase(UserDatabase udb) throws Exception {
Query query = em.get().createQuery("from Tenant t where t.userDatabase = :udb");
query.setParameter("udb", udb);
return (List<Tenant>) query.getResultList();
}
@SuppressWarnings("unchecked")
public List<ContainerTenant> findContainerTenants(Container ofContainer) throws Exception {
Query query = em.get().createQuery("from ContainerTenant ct where ct.container = :cont");
query.setParameter("cont", ofContainer);
return (List<ContainerTenant>) query.getResultList();
}
@SuppressWarnings("unchecked")
public List<Priviledge> findPriviledgesOnTenant(Tenant t) throws PEException {
Query query = em.get().createQuery("from Priviledge p where p.tenant = :t");
query.setParameter("t", t);
return (List<Priviledge>) query.getResultList();
}
public Priviledge findMatchingPrivilege(int userid, String name) throws PEException {
// Query query =
// em.createQuery("from Priviledge p where (p.database.name = :name or p.tenant.externalID = :name) and p.user = :user");
Query query = em
.get()
.createQuery(
"select p from Priviledge p left join fetch p.database ud left join fetch p.tenant t where p.user.id = :userid and (ud.name = :name or t.externalID = :name)");
query.setParameter("userid", userid);
query.setParameter("name", name);
@SuppressWarnings("unchecked")
List<Priviledge> res = query.getResultList();
return onlyOne(res, "Privilege", userid + "/" + name, false);
}
public boolean findGlobalPrivilege(User user) throws PEException {
Query query = em.get().createQuery(
"from Priviledge p where p.user = :user and p.database is null and p.tenant is null");
query.setParameter("user", user);
@SuppressWarnings("unchecked")
List<Priviledge> res = query.getResultList();
Priviledge candidate = onlyOne(res, "Privilege", user.getName() + "/*", false);
return candidate != null;
}
@SuppressWarnings("unchecked")
public List<TableVisibility> findMatchingTenantRecords(UserTable ut) {
Query query = em.get().createQuery("from TableVisibility tv where tv.table = :ut ");
query.setParameter("ut", ut);
return (List<TableVisibility>) query.getResultList();
}
@SuppressWarnings("unchecked")
public List<String> findTenantTableNames(Tenant t) {
Query query = em.get().createQuery("select ut.name from UserTable ut where ut.name like :tname");
query.setParameter("tname", "_" + t.getId() + "%");
return (List<String>) query.getResultList();
}
@SuppressWarnings("unchecked")
public List<TableVisibility> findMatchingScopes(Shape matchShape, TableState matchState) throws PEException {
Query query = em
.get()
.createQuery(
"from TableVisibility tv where tv.table.state = :state and tv.table.shape = :shape order by tv.table.name");
query.setParameter("shape", matchShape);
query.setParameter("state", matchState);
return (List<TableVisibility>) query.getResultList();
}
@SuppressWarnings("unchecked")
public List<UserTable> findMatchingTables(Shape matchShape, TableState matchState) throws PEException {
Query query = em.get().createQuery(
"from UserTable ut where ut.shape = :matchShape and ut.state = :matchState order by ut.name");
query.setParameter("matchShape", matchShape);
query.setParameter("matchState", matchState);
return (List<UserTable>) query.getResultList();
}
public Shape findShape(UserDatabase indb, String logicalName, String definition, String typeHash)
throws PEException {
Query query = em
.get()
.createQuery(
"from Shape s where s.userDatabase = :indb and s.tableDefinition = :definition and s.name = :logicalName and s.typeHash = :typeHash");
query.setParameter("indb", indb);
query.setParameter("definition", definition);
query.setParameter("logicalName", logicalName);
query.setParameter("typeHash", typeHash);
@SuppressWarnings("unchecked")
List<Shape> results = query.getResultList();
return onlyOne(results, "Shape", indb.getName() + "/" + logicalName, false);
}
@SuppressWarnings("unchecked")
public List<UserTable> findMatchingTables(UserDatabase indb, String logicalName, String definition) {
Query query = em
.get()
.createQuery(
"from UserTable ut where ut.userDatabase = :indb and ut.shape.tableDefinition = :definition and ut.shape.name = :logicalName and ut.stale != 1");
query.setParameter("indb", indb);
query.setParameter("definition", definition);
query.setParameter("logicalName", logicalName);
return (List<UserTable>) query.getResultList();
}
@SuppressWarnings("unchecked")
public List<Shape> findShapesOnDB(UserDatabase indb) {
Query query = em.get().createQuery("from Shape s where s.userDatabase = :indb");
query.setParameter("indb", indb);
return (List<Shape>) query.getResultList();
}
public UserTable findUserTable(int dbid, String tableName, boolean quoted) throws PEException {
Query query = em.get().createQuery("from UserTable ut where ut.name = :name and ut.userDatabase.id = :dbid");
query.setParameter("name", tableName);
query.setParameter("dbid", dbid);
@SuppressWarnings("unchecked")
List<UserTable> res = query.getResultList();
return onlyOne(res, "UserTable", Integer.toString(dbid), tableName, false, quoted);
}
@SuppressWarnings("unchecked")
public List<UserTable> findTablesOnGroup(String groupName) {
Query query = em.get().createQuery("from UserTable ut where ut.persistentGroup.name = :groupName");
query.setParameter("groupName",groupName);
return query.getResultList();
}
@SuppressWarnings("unchecked")
public UserView findView(String viewName, String dbName) throws PEException {
Query query = em.get().createQuery(
"from UserView uv where uv.table.name = :viewName and uv.table.userDatabase.name = :dbName");
query.setParameter("viewName", viewName);
query.setParameter("dbName", dbName);
List<UserView> res = query.getResultList();
return onlyOne(res, "UserView", dbName + "." + viewName, false);
}
public void recoverTransactions() {
// DBConnectionParameters dbp = new DBConnectionParameters(Host.getProperties());
// for (TransactionRecord txn : findAltemplate(TransactionRecord.class)) {
// txn.recover2PC(this, dbp.getUserAuthentication());
// }
}
@SuppressWarnings("unchecked")
public List<UserTable> findContainerMemberTables(String containerName) throws PEException {
Query query = em.get().createQuery("from UserTable ut where ut.container.name = :name");
query.setParameter("name", containerName);
return query.getResultList();
}
@SuppressWarnings("unchecked")
public List<PersistentTemplate> findMatchTemplates() throws PEException {
Query query = em.get().createQuery("from PersistentTemplate pt where pt.dbmatch is not null order by pt.name");
return query.getResultList();
}
public PersistentTemplate findTemplate(String name, boolean except) throws PEException {
return findByName(PersistentTemplate.class, name, except);
}
public RawPlan findRawPlan(String name, boolean except) throws PEException {
return findByName(RawPlan.class, name, except);
}
@SuppressWarnings("unchecked")
public List<RawPlan> findAllEnabledRawPlans() throws PEException {
Query q = em.get().createQuery("from RawPlan rp where rp.enabled = 1");
return q.getResultList();
}
public abstract class EntityUpdater {
public abstract CatalogEntity update() throws Throwable;
/**
* A utility method that encapsulates the logic of updating a catalog
* entity
*
* @return
* @throws Throwable
*/
public CatalogEntity execute() throws Throwable {
CatalogEntity ret = null;
begin();
try {
ret = update();
commit();
} catch (Throwable t) {
rollback(t);
throw t;
}
return ret;
}
}
public abstract class EntityGenerator extends EntityUpdater {
public abstract CatalogEntity generate() throws Throwable;
@Override
public CatalogEntity update() throws Throwable {
CatalogEntity entity = generate();
if (entity != null)
persistToCatalog(entity);
return entity;
}
}
public PersistentTemplate createTemplate(String name, String template, String match, String comment) {
PersistentTemplate t = new PersistentTemplate(name, template, match, comment);
em.get().persist(t);
return t;
}
public SiteInstance createSiteInstance(String name, String url, String user, String password) {
SiteInstance si = new SiteInstance(name, url, user, password);
em.get().persist(si);
return si;
}
public SiteInstance createSiteInstance(String name, String url, String user, String password, boolean isMaster,
String status) {
SiteInstance si = new SiteInstance(name, url, user, password, isMaster, status);
em.get().persist(si);
return si;
}
public SiteInstance findSiteInstance(String name) throws PEException {
return findSiteInstance(name, true);
}
public SiteInstance findSiteInstance(String name, boolean except) throws PEException {
return findByName(SiteInstance.class, name, except);
}
public UserTable loadTable(UserTable foo) {
UserTable theTable = foo;
if (!em.get().getEntityManagerFactory().getPersistenceUnitUtil().isLoaded(theTable, "userColumns")) {
Query query = em.get().createQuery("from UserTable ut JOIN FETCH ut.userColumns WHERE ut.id = :id");
query.setParameter("id", theTable.getId());
theTable = (UserTable) query.getResultList().get(0);
}
return theTable;
}
@SuppressWarnings("unchecked")
public List<ServerRegistration> findAllRegisteredServers() {
Query query = em.get().createQuery("from ServerRegistration");
return query.getResultList();
}
@SuppressWarnings("unchecked")
public ServerRegistration findServerRegistrationByAddress(String ourAddress) throws PENotFoundException {
Query query = em.get().createQuery("from ServerRegistration where ipAddress = :ipAddress");
query.setParameter("ipAddress", ourAddress);
List<ServerRegistration> results = query.getResultList();
return onlyOne(results, ourAddress, "ipAddress", true);
}
public Container findContainer(String containerName) throws PEException {
return findContainer(containerName, true);
}
public Container findContainer(String containerName, boolean except) throws PEException {
return findByName(Container.class, containerName, except);
}
@SuppressWarnings("unchecked")
public ContainerTenant findContainerTenant(String containerName, String disc) throws PEException {
Query query = em.get().createQuery(
"from ContainerTenant ct where ct.container.name = :containerName and ct.discriminant = :disc");
query.setParameter("containerName", containerName);
query.setParameter("disc", disc);
return onlyOne((List<ContainerTenant>) query.getResultList(), "ContainerTenant", containerName + "/" + disc,
false);
}
@SuppressWarnings("unchecked")
public List<UserTable> findBaseTables(UserDatabase within) throws PEException {
Query query = em.get().createQuery(
"from UserTable ut where ut.userDatabase = :udb and ut.container.baseTable = ut");
query.setParameter("udb", within);
return (List<UserTable>) query.getResultList();
}
@SuppressWarnings("unchecked")
public List<UserDatabase> findDatabasesWithin(Container cont) throws PEException {
Query query = em.get().createQuery(
"select distinct ut.userDatabase from UserTable ut where ut.container = :cont");
query.setParameter("cont", cont);
return (List<UserDatabase>) query.getResultList();
}
// this restricts the search to this server
public List<TemporaryTable> findLocalUserlandTemporaryTables(Integer connID, String dbName, String tableName) {
return findUserlandTemporaryTables(GroupManager.getCoordinationServices().getMemberAddress().toString(),connID,dbName, tableName);
}
// values not specified won't be queried on - so null,null,null returns all temp tables
// and foo, null, null returns all temp tables on server foo
@SuppressWarnings("unchecked")
public List<TemporaryTable> findUserlandTemporaryTables(String serverName, Integer connID, String dbName, String tableName) {
StringBuilder hql = new StringBuilder();
hql.append("from TemporaryTable tt ");
Query query = null;
if (serverName == null && connID == null && tableName == null) {
query = em.get().createQuery(hql.toString());
} else {
hql.append("where ");
List<String> filters = new ArrayList<String>();
if (serverName != null)
filters.add("tt.server = :serverName");
if (connID != null)
filters.add("tt.sessionID = :connID");
if (dbName != null)
filters.add("tt.db = :dbName");
if (tableName != null)
filters.add("tt.name = :tableName");
hql.append(Functional.join(filters, " and "));
query = em.get().createQuery(hql.toString());
if (serverName != null)
query.setParameter("serverName",serverName);
if (connID != null)
query.setParameter("connID", connID);
if (dbName != null)
query.setParameter("dbName",dbName);
if (tableName != null)
query.setParameter("tableName", tableName);
}
return (List<TemporaryTable>)query.getResultList();
}
public void cleanupUserlandTemporaryTables(String serverName) {
Query query = em.get().createQuery("delete from TemporaryTable where server = :name");
query.setParameter("name", serverName);
query.executeUpdate();
}
@SuppressWarnings("unchecked")
public List<UserTable> findTablesWithUnresolvedFKsTargeting(String dbName, String tabName) {
Query query = em
.get()
.createQuery(
"select distinct k.userTable from Key k where k.constraint = 'FOREIGN' and k.referencedSchemaName = :dbName and k.referencedTableName = :tableName");
query.setParameter("dbName", dbName);
query.setParameter("tableName", tabName);
return (List<UserTable>) query.getResultList();
}
@SuppressWarnings("unchecked")
public List<UserTable> findTablesWithFKsReferencing(int tabID) {
Query query = em
.get()
.createQuery(
"select distinct k.userTable from Key k where k.constraint = 'FOREIGN' and k.referencedTable.id = :tabID");
query.setParameter("tabID", tabID);
return (List<UserTable>) query.getResultList();
}
@SuppressWarnings("unchecked")
public List<TableVisibility> findScopesWithFKsReferencing(int tabID, int tenantID) {
Query query = em.get().createQuery(
"select tv from TableVisibility tv, Key k "
+ "where k.constraint = 'FOREIGN' and k.referencedTable.id = :tabID "
+ "and tv.ofTenant.id = :tenantID and k.userTable = tv.table");
query.setParameter("tabID", tabID);
query.setParameter("tenantID", tenantID);
return (List<TableVisibility>) query.getResultList();
}
@SuppressWarnings("unchecked")
public List<TableVisibility> findScopesWithUnresolvedFKsTargeting(String dbName, String tabName, int tenant) {
/*
* I started with this sql: select s.scope_id from scope s inner join
* user_table ut on s.scope_table_id = ut.table_id inner join user_key
* uk on ut.table_id = uk.user_table_id where uk.forward_schema_name = ?
* and uk.forward_table_name = ? and s.scope_tenant_id = ?;
*
* using this query, hql will cause us to deref the collection of keys
* which would not be optimal
*/
Query query = em.get().createQuery(
"select tv from TableVisibility tv, Key k "
+ "where k.referencedSchemaName = :dbName and k.referencedTableName = :tabName "
+ "and tv.ofTenant.id = :tenant " + "and tv.table = k.userTable");
query.setParameter("tenant", tenant);
query.setParameter("dbName", dbName);
query.setParameter("tabName", tabName);
return (List<TableVisibility>) query.getResultList();
}
// given a backing table and a tenant, tell me all the scopes in that tenant
// of the tables it references.
@SuppressWarnings("unchecked")
public List<TableVisibility> findScopesForFKTargets(int tableID, int tenantID) {
/*
* here is my raw query: select s.scope_id from user_key uk inner join
* scope s on s.scope_table_id = uk.user_table_id where
* uk.referenced_table = :tableID and s.scope_tenant_id = :tenantID
*/
Query query = em
.get()
.createQuery(
"select tv from TableVisibility tv, Key uk where tv.table = uk.userTable and uk.referencedTable.id = :tableID and tv.ofTenant.id = :tenantID");
query.setParameter("tableID", tableID);
query.setParameter("tenantID", tenantID);
return (List<TableVisibility>) query.getResultList();
}
public Key findForeignKey(UserDatabase db, Integer tenantID, String name, String constraintName, boolean except)
throws PEException {
if (StringUtils.isBlank(name) && StringUtils.isBlank(constraintName)) {
return null;
}
StringBuffer qry = new StringBuffer("from Key k ");
if (tenantID != null)
qry.append(", TableVisibility tv ");
qry.append("where k.constraint = 'FOREIGN' and k.userTable.userDatabase = :db ");
if (!StringUtils.isBlank(name))
qry.append("and k.name = :name");
if (!StringUtils.isBlank(constraintName))
qry.append("and k.constraintName = :constraintName");
if (tenantID != null)
qry.append(" and tv.ofTenant.id = :tenantID and tv.table = k.userTable ");
Query query = em.get().createQuery(qry.toString());
query.setParameter("db", db);
if (!StringUtils.isBlank(name))
query.setParameter("name", name);
if (!StringUtils.isBlank(constraintName))
query.setParameter("constraintName", constraintName);
if (tenantID != null)
query.setParameter("tenantID", tenantID);
@SuppressWarnings("unchecked")
List<Key> keys = query.getResultList();
return onlyOne(keys, "Foreign Keys", name + "/" + constraintName, except);
}
public Key findKey(String dbName, String encTabName, String keyName) throws PEException {
Query query = em
.get()
.createQuery(
"from Key k where k.name = :keyName and k.userTable.name = :encTabName and k.userTable.userDatabase.name = :dbName");
query.setParameter("dbName", dbName);
query.setParameter("encTabName", encTabName);
query.setParameter("keyName", keyName);
@SuppressWarnings("unchecked")
List<Key> keys = query.getResultList();
return onlyOne(keys, "Specific key", dbName + "." + encTabName + "/" + keyName, false);
}
public UserTrigger findTrigger(String name, String dbName) throws PEException {
Query query = em.get().createQuery("from UserTrigger ut where ut.name = :name and ut.table.userDatabase.name = :dbname");
query.setParameter("name",name);
query.setParameter("dbname",dbName);
List<UserTrigger> trigs = query.getResultList();
return onlyOne(trigs, "Triggers", dbName + "." + name,false);
}
public void deleteAllServerRegistration() {
Query q = em.get().createQuery("delete from ServerRegistration");
q.executeUpdate();
}
// for autoupdate
public EntityManager getEntityManager() {
return em.get();
}
public static DataSource getCatalogDS() {
@SuppressWarnings("unchecked")
Set<DataSource> c3p0pools = C3P0Registry.allPooledDataSources();
return c3p0pools.iterator().next();
}
public enum CatalogDAOFactory {
INSTANCE;
EntityManagerFactory emf = null;
Map<Class<?>, CachedCatalogLookup<? extends CatalogEntity>> lookupCacheMap
= new HashMap<Class<?>, CachedCatalogLookup<? extends CatalogEntity>>();
public static CatalogDAO newInstance() {
return new CatalogDAO(INSTANCE.emf.createEntityManager());
}
public static CatalogDAO newInstance(Properties p) throws PEException {
return new CatalogDAO(INSTANCE.getEMF(p).createEntityManager(fixProperties(p)));
}
public static boolean isSetup() {
return INSTANCE.emf != null ? true : false;
}
public static void setup(Properties p) throws PEException {
INSTANCE.emf = Persistence.createEntityManagerFactory("DveCatalog", fixProperties(p));
}
private static Properties fixProperties(Properties p) throws PEException {
final String url = p.getProperty(DBHelper.CONN_URL);
// In production we don't have a default value for the catalog URL
if(StringUtils.isEmpty(url))
throw new PEException("Value for " + DBHelper.CONN_URL + " not specified in properties");
final Properties props = new Properties(p);
props.putAll(p);
final PEUrl peUrl = CatalogURL.buildCatalogBaseUrlFrom(url);
final String dbName = props.getProperty(DBHelper.CONN_DBNAME);
props.remove(DBHelper.CONN_DBNAME);
peUrl.setPath(dbName);
props.setProperty(DBHelper.CONN_URL, peUrl.getURL());
props.remove("hibernate.hbm2ddl.auto");
return props;
}
public static void shutdown() {
if (Boolean.getBoolean("CatalogDAO.printCacheStatistics")) {
CacheManager cm = CacheManager.getInstance();
for (String s : cm.getCacheNames()) {
if (s.matches("com.tesora.dve.*")) {
Statistics stats = cm.getCache(s).getStatistics();
System.out.println("Cache " + s + ": " + stats.getObjectCount() +
" items (" + stats.getCacheHits() + " hits, " + stats.getCacheMisses() + " misses)");
}
}
}
INSTANCE.lookupCacheMap.clear();
if (INSTANCE.emf != null)
INSTANCE.emf.close();
INSTANCE.emf = null;
}
public static void clearCache() {
CacheManager.getInstance().clearAll();
}
EntityManagerFactory getEMF(Properties p) throws PEException {
if (emf == null)
setup(p);
return emf;
}
public CachedCatalogLookup<? extends CatalogEntity>
getLookupCache(Class<? extends CatalogEntity> cacheClass, String columnName) {
CachedCatalogLookup<? extends CatalogEntity> lookupCache = null;
// it's possible to have a race condition here between threads within the same jvm
// in particular, between a containsKey call and a get call the cache could be cleared
// leading to npes in callers - so instead if we don't find a cache, create a new one
// and return that
lookupCache = lookupCacheMap.get(cacheClass);
if (lookupCache == null) {
lookupCache = new CachedCatalogLookup<CatalogEntity>(cacheClass, columnName);
if (cacheClass.equals(RangeTableRelationship.class))
lookupCache = new RangeTableRelationshipCacheLookup(cacheClass);
lookupCacheMap.put(cacheClass, lookupCache);
}
return lookupCache;
}
public void clearLookupCache(Class<?> cacheClass) {
lookupCacheMap.remove(cacheClass);
}
}
}