package com.tesora.dve.sql.schema.cache;
/*
* #%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.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.google.common.cache.CacheStats;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.sql.schema.PERawPlan;
import com.tesora.dve.sql.schema.SchemaContext;
import com.tesora.dve.sql.transform.execution.ConnectionValuesMap;
import com.tesora.dve.sql.transform.execution.RootExecutionPlan;
import com.tesora.dve.sql.util.Functional;
import com.tesora.dve.sql.util.Pair;
import com.tesora.dve.sql.util.UnaryFunction;
public abstract class PlanCache {
public enum RawLoadState {
// don't bother checking or else the raw plans are loaded
LOADED,
// next time we have a context handy, load raw plans
LOAD_REQUIRED
}
private final ConcurrentHashMap<PlanCacheKey, RegularCachedPlan> rawPlans;
private final int rawSize;
private RawLoadState rawState;
public PlanCache(int maxRaw) {
rawPlans = new ConcurrentHashMap<PlanCacheKey, RegularCachedPlan>();
rawSize = maxRaw;
rawState = RawLoadState.LOAD_REQUIRED;
}
public final boolean isEmpty() {
return isMainEmpty() && (rawSize == 0 || (rawPlans.isEmpty() && rawState == RawLoadState.LOADED));
}
public abstract boolean isMainEmpty();
public final long getSize() {
return getMainSize() + (rawSize == 0 ? 0 : rawPlans.size());
}
public abstract long getMainSize();
public final CacheStats getStats() {
return getMainStats();
}
public final void resetStats() {
resetMainStats();
}
public abstract CacheStats getMainStats();
public abstract void resetMainStats();
public final void invalidate(PlanCacheKey pck) {
invalidateMain(pck);
if (rawSize > 0 && rawPlans.containsKey(pck))
onRawPlanEvent();
}
public abstract void invalidateMain(PlanCacheKey pck);
public final RegularCachedPlan get(SchemaContext sc, PlanCacheKey pck) {
if (rawSize > 0) {
if (rawState == RawLoadState.LOAD_REQUIRED)
maybeLoadRawPlans(sc);
RegularCachedPlan plan = rawPlans.get(pck);
if (plan != null) return plan;
}
return getMain(pck);
}
public final void onRawPlanEvent() {
if (rawSize == 0) return;
synchronized(this) {
rawState = RawLoadState.LOAD_REQUIRED;
}
}
public abstract RegularCachedPlan getMain(PlanCacheKey pck);
public final void put(RegularCachedPlan rcp) {
putMain(rcp);
}
public abstract void putMain(RegularCachedPlan rcp);
public abstract Map<PlanCacheKey, RegularCachedPlan> getMainMap();
public Map<PlanCacheKey, RegularCachedPlan> getROMap() {
if (rawSize == 0)
return getMainMap();
LinkedHashMap<PlanCacheKey, RegularCachedPlan> out = new LinkedHashMap<PlanCacheKey,RegularCachedPlan>();
out.putAll(rawPlans);
out.putAll(getMainMap());
return out;
}
private void maybeLoadRawPlans(final SchemaContext sc) {
if (sc == null || rawSize == 0) return;
List<PERawPlan> plans = sc.findEnabledRawPlans();
List<RegularCachedPlan> built = Functional.apply(plans, new UnaryFunction<RegularCachedPlan,PERawPlan>() {
@Override
public RegularCachedPlan evaluate(PERawPlan object) {
return object.getPlan(sc);
}
});
synchronized(rawPlans) {
if (rawState == RawLoadState.LOADED) {
// may not have to do anything unless the size of the raw plan cache is different than
// that of built
if (built.size() == rawPlans.size())
return;
}
rawPlans.clear();
for(int i = 0; i < built.size() && i < rawSize; i++) {
RegularCachedPlan rcp = built.get(i);
rawPlans.put(rcp.getKey(),rcp);
}
rawState = RawLoadState.LOADED;
}
}
public static void registerPreparedStatementPlan(SchemaContext sc, CachedPreparedStatement cps, String prepareSQL, int connid, String stmtID, boolean reregister) throws PEException {
sc.getSource().putPreparedStatement(cps, connid, stmtID, prepareSQL, reregister);
}
public static void destroyPreparedStatement(SchemaContext sc, String stmtID) {
sc.getSource().clearPreparedStatement(sc.getConnection().getConnectionId(),stmtID);
}
public static Pair<RootExecutionPlan,ConnectionValuesMap> bindPreparedStatement(SchemaContext sc, String stmtID, List<Object> params) throws PEException {
CachedPreparedStatement cps = sc.getSource().getPreparedStatement(sc, sc.getConnection().getConnectionId(), stmtID);
return cps.rebuildPlan(sc, params);
}
}