package com.tesora.dve.sql.schema.mt; /* * #%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.Collection; import java.util.HashSet; import java.util.List; import com.tesora.dve.common.PEConstants; import com.tesora.dve.common.catalog.CatalogEntity; import com.tesora.dve.common.catalog.ITenant; import com.tesora.dve.common.catalog.TableVisibility; import com.tesora.dve.common.catalog.Tenant; import com.tesora.dve.exceptions.PEException; import com.tesora.dve.sql.SchemaException; import com.tesora.dve.sql.ParserException.Pass; import com.tesora.dve.sql.node.expression.MTTableInstance; import com.tesora.dve.sql.node.expression.TableInstance; import com.tesora.dve.sql.schema.LockInfo; import com.tesora.dve.sql.schema.Name; import com.tesora.dve.sql.schema.PEDatabase; import com.tesora.dve.sql.schema.PETable; import com.tesora.dve.sql.schema.Persistable; import com.tesora.dve.sql.schema.SchemaContext; import com.tesora.dve.sql.schema.StructuralUtils; import com.tesora.dve.sql.schema.UnqualifiedName; import com.tesora.dve.sql.schema.cache.CacheAwareLookup; import com.tesora.dve.sql.schema.cache.CacheSegment; import com.tesora.dve.sql.schema.cache.NamedEdge; import com.tesora.dve.sql.schema.cache.SchemaCacheKey; import com.tesora.dve.sql.schema.cache.SchemaEdge; import com.tesora.dve.sql.schema.mt.TableScope.ScopeCacheKey; public class PETenant extends Persistable<PETenant, Tenant> implements IPETenant { private String extID; private String description; private boolean suspended; private long tenantID; private SchemaEdge<PEDatabase> ofDB; private CacheAwareLookup<TableScope> lookup; private boolean loaded = false; public PETenant(SchemaContext pc, PEDatabase onDatabase, String extID, String desc) { this(pc, onDatabase, extID, desc, -1); } // used in the tests only! @SuppressWarnings("unchecked") public PETenant(SchemaContext pc, PEDatabase onDatabase, String extID, String desc, int requestedID) { super(getTenantKey(extID,requestedID)); this.extID = extID; this.description = desc; this.suspended = false; this.tenantID = requestedID; this.ofDB = StructuralUtils.buildEdge(pc,onDatabase,false); if (requestedID != -1 && pc.getCatalog().isPersistent()) throw new SchemaException(Pass.SECOND, "Wrong ctor for PETenant"); lookup = new CacheAwareLookup<TableScope>(true, true); loaded = true; } public String getExternalID() { return extID; } @Override public Name getName() { return new UnqualifiedName(extID); } @Override public String getUniqueIdentifier() { return getName().get(); } public String getDescription() { return description; } public boolean isSuspended() { return suspended; } public void setSuspended(boolean v) { suspended = v; } public PEDatabase getDatabase(SchemaContext sc) { return ofDB.get(sc); } public TableScope lookupScope(SchemaContext pc, Name n, LockInfo lockInfo) { return lazyLookupScope(pc, n, lockInfo); } private TableScope lazyLookupScope(SchemaContext pc, Name n, LockInfo lockType) { TableScope targ = lookup.lookup(pc, n); if (targ == null) { ScopeCacheKey sck = TableScope.getScopeKey(this, n); sck.acquireLock(pc, lockType); targ = pc.findScope(sck); if (targ == null) return null; lookup.add(pc, targ,true); } else { targ.getCacheKey().acquireLock(pc, lockType); } return targ; } public PETable lookup(SchemaContext pc, Name n, LockInfo info) { TableScope ts = lookupScope(pc,n, info); if (ts == null) return null; return ts.getTable(pc); } public TableInstance build(SchemaContext pc, Name n, LockInfo info) { TableScope ts = lookupScope(pc,n, info); if (ts == null) return null; return new MTTableInstance(ts.getTable(pc),ts,pc.getOptions().isResolve()); } public TableScope setVisible(SchemaContext sc, PETable tab, Name localName, Long autoIncOffset, LockInfo info) { TableScope already = lookupScope(sc, localName, info); if (already != null) return null; TableScope ts = new TableScope(sc, tab, this, autoIncOffset, localName); lookup.add(sc, ts,false); return ts; } public Collection<TableScope> getTableScopes(SchemaContext sc) { checkLoaded(sc); ArrayList<TableScope> scopes = new ArrayList<TableScope>(); for(NamedEdge<TableScope> ne : lookup.getValues()) { TableScope ts = ne.getEdge().get(sc); if (ts == null) continue; scopes.add(ts); } return scopes; } @Override public Long getTenantID() { return new Long(tenantID); } @Override public boolean isGlobalTenant() { return PEConstants.LANDLORD_TENANT.equals(extID); } @SuppressWarnings("unchecked") private PETenant(Tenant ten, SchemaContext sc) { super(getTenantKey(ten)); sc.startLoading(this, ten); setPersistent(sc,ten,ten.getId()); this.extID = ten.getExternalTenantId(); this.description = ten.getDescription(); this.suspended = ten.isSuspended(); this.tenantID = ten.getId(); this.ofDB = StructuralUtils.buildEdge(sc,PEDatabase.load(ten.getDatabase(), sc),true); this.lookup = new CacheAwareLookup<TableScope>(true, true); this.loaded = false; sc.finishedLoading(this, ten); } private void checkLoaded(SchemaContext pc) { if (this.loaded) return; this.loaded = true; if (pc.isPersistent()) { for(TableVisibility tv : getPersistent(pc).getScoping()) { TableScope ts = TableScope.load(tv, pc); lookup.add(pc,ts,true); } } } public static PETenant load(Tenant ten, SchemaContext sc) { PETenant peu = (PETenant)sc.getLoaded(ten,getTenantKey(ten)); if (peu == null) peu = new PETenant(ten, sc); return peu; } @Override protected Class<? extends CatalogEntity> getPersistentClass() { return Tenant.class; } @Override protected Tenant lookup(SchemaContext pc) throws PEException { if (!pc.isPersistent()) return null; return pc.getCatalog().findTenant(extID); } @Override protected Tenant createEmptyNew(SchemaContext pc) throws PEException { return new Tenant(ofDB.get(pc).persistTree(pc), extID, description); } @Override protected void populateNew(SchemaContext pc, Tenant p) throws PEException { if (suspended) p.setSuspended(); else p.setResumed(); } @Override public ITenant getPersistentTenant(SchemaContext sc) throws PEException { return getPersistent(sc); } @Override protected void updateExisting(SchemaContext pc, Tenant p) throws PEException { populateNew(pc, p); } @Override protected Persistable<PETenant, Tenant> load(SchemaContext pc, Tenant p) throws PEException { return null; } @Override protected int getID(Tenant p) { return p.getId(); } @Override protected String getDiffTag() { return null; } public UnqualifiedName buildPrivateTableName(SchemaContext pc, Name n) { List<String> existingNames = pc.findTenantTableNames(this); HashSet<String> such = new HashSet<String>(existingNames); StringBuilder buf = new StringBuilder(); buf.append("_").append(tenantID).append(n.get()); UnqualifiedName candidate = new UnqualifiedName(buf.toString(),n.isQuoted()); String nn = null; int pcounter = 0; while(nn == null) { nn = candidate.getUnqualified().get() + pcounter; if (such.contains(nn)) { pcounter++; nn = null; } } return new UnqualifiedName(nn); } public static SchemaCacheKey<PETenant> getTenantKey(Name tenantName) { return getTenantKey(tenantName.getUnquotedName().get(),-1); } public static SchemaCacheKey<PETenant> getTenantKey(String n, int tenantID) { return new TenantCacheKey(n,tenantID); } public static SchemaCacheKey<PETenant> getTenantKey(Tenant tenant) { return getTenantKey(tenant.getExternalTenantId(),tenant.getId()); } public static class TenantCacheKey extends SchemaCacheKey<PETenant> { /** * */ private static final long serialVersionUID = 1L; private final String tenantName; private final int tenantID; public TenantCacheKey(String n, int tenID) { super(); tenantName = n; tenantID = tenID; } @Override public int hashCode() { return initHash(PETenant.class,tenantName.hashCode()); } @Override public String toString() { return "PETenant:" + tenantName; } public String getTenantName() { return tenantName; } public int getTenantID() { return tenantID; } @Override public boolean equals(Object o) { if (o instanceof TenantCacheKey) { TenantCacheKey oth = (TenantCacheKey) o; return tenantName.equals(oth.tenantName); } return false; } @Override public PETenant load(SchemaContext sc) { Tenant ten = null; if (sc.getCatalog().isPersistent() && tenantID > 0) ten = (Tenant) sc.getCatalog().findByKey(Tenant.class,tenantID); else ten = sc.getCatalog().findTenant(tenantName); if (ten == null) return null; return PETenant.load(ten, sc); } @Override public CacheSegment getCacheSegment() { return CacheSegment.TENANT; } @Override public Collection<SchemaCacheKey<?>> getCascades(Object ten) { PETenant tenant = (PETenant) ten; return tenant.lookup.getCascades(); } } }