package com.tesora.dve.queryplan; /* * #%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.List; import org.apache.log4j.Logger; import com.tesora.dve.common.catalog.DistributionModel; import com.tesora.dve.common.catalog.PersistentDatabase; import com.tesora.dve.common.catalog.StorageGroup; import com.tesora.dve.common.catalog.StorageSite; import com.tesora.dve.common.logutil.ExecutionLogger; import com.tesora.dve.common.logutil.LogSubject; import com.tesora.dve.db.DBEmptyTextResultConsumer; import com.tesora.dve.db.DBResultConsumer; import com.tesora.dve.distribution.IKeyValue; import com.tesora.dve.exceptions.PEException; import com.tesora.dve.variables.ResultCollector; import com.tesora.dve.server.connectionmanager.SSConnection; import com.tesora.dve.server.messaging.GetWorkerRequest; import com.tesora.dve.server.messaging.SQLCommand; import com.tesora.dve.sql.schema.SchemaContext.DistKeyOpType; import com.tesora.dve.sql.util.Functional; import com.tesora.dve.sql.util.UnaryPredicate; import com.tesora.dve.worker.WorkerGroup; import com.tesora.dve.worker.WorkerManager; /** * Baseclass of <b>QueryStep</b> operations, which include, but are not limited to * {@link QueryStepInsertByKeyOperation}, * {@link QueryStepSelectAllOperation} and {@link QueryStepRedistOperation}. * @author mjones * */ public abstract class QueryStepOperation implements LogSubject { static final Logger logger = Logger.getLogger(QueryStepOperation.class); protected final List<QueryStepOperation> reqs; protected final StorageGroup sg; public QueryStepOperation(StorageGroup sg) throws PEException { if (sg == null) throw new PEException("Invalid QSO construction: null storage group"); this.sg = sg; this.reqs = new ArrayList<QueryStepOperation>(); } public void addRequirement(QueryStepOperation qso) { if (qso == this) throw new IllegalArgumentException("Invalid required step: self"); reqs.add(qso); } public void execute(ExecutionState state, DBResultConsumer resultConsumer) throws Throwable { executeRequirements(state); executeOperation(state,resultConsumer); } final void executeRequirements(ExecutionState estate) throws Throwable { for(QueryStepOperation qso : reqs) qso.execute(estate, DBEmptyTextResultConsumer.INSTANCE); } void executeOperation(ExecutionState execState, DBResultConsumer resultConsumer) throws Throwable { SSConnection ssCon = execState.getConnection(); try { ExecutionLogger beforeLogger = ssCon.getExecutionLogger().getNewLogger("BeforeStepExec"); WorkerGroup wg = null; if (requiresImplicitCommit()) ssCon.implicitCommit(); if (requiresWorkers()) { if (sg == nullStorageGroup) throw new PEException("No storage group specified but one required"); wg = ssCon.getWorkerGroupAndPushContext(sg,getContextDatabase()); } ExecutionLogger slowQueryLogger = null; try { beforeLogger.end(); if (logger.isDebugEnabled()) logger.debug("QueryStep executes " + toString()); slowQueryLogger = ssCon.getExecutionLogger().getNewLogger(this); executeSelf(execState, wg, resultConsumer); } finally { ExecutionLogger afterLogger = null; try { slowQueryLogger.end(); afterLogger = ssCon.getExecutionLogger().getNewLogger("AfterStepExec"); } finally { try { if (wg != null) ssCon.returnWorkerGroupAndPopContext(wg); } finally { if (afterLogger != null) afterLogger.end(); } } } } finally { } } protected SQLCommand bindCommand(ExecutionState in, SQLCommand sqlc) { return sqlc.getLateResolvedCommand(in); } /** * Called by <b>QueryStep</b> to execute the operation. Any results * of the operation will be returned in an instance of {@link ResultCollector}. * * @param ssCon user's connection * @param wg provisioned {@link WorkerGroup} against which to execute the step * @param resultConsumer * @return {@link ResultCollector} with results, or <b>null</b> * @throws JMSException * @throws PEException * @throws Throwable */ public abstract void executeSelf(ExecutionState execState, WorkerGroup wg, DBResultConsumer resultConsumer) throws Throwable; public boolean requiresTransactionSelf() { return true; } boolean requiresTransaction() { if (requiresTransactionSelf()) return true; return Functional.any(reqs, new UnaryPredicate<QueryStepOperation>() { @Override public boolean test(QueryStepOperation object) { return object.requiresTransactionSelf(); } }); } public boolean hasRequirements() { return !reqs.isEmpty(); } /** * Does this query step require workers? Default is true. Some DDL operations are catalog-only, and * thus do not require workers. */ public boolean requiresWorkers() { return true; } /** * Returns true if the QueryStepOperation causes any inflight transactions to be committed * first. */ public boolean requiresImplicitCommit() { return false; } @Override public String describeForLog() { return this.getClass().getSimpleName(); } public PersistentDatabase getContextDatabase() { return null; } public final StorageGroup getStorageGroup() { return sg; } protected static final StorageGroup nullStorageGroup = new StorageGroup() { @Override public String getName() { fail(); return null; } @Override public int sizeForProvisioning() throws PEException { fail(); return 0; } @Override public void provisionGetWorkerRequest(GetWorkerRequest getWorkerRequest) throws PEException { fail(); } @Override public void returnWorkerSites(WorkerManager workerManager, Collection<? extends StorageSite> groupSites) throws PEException { fail(); } @Override public boolean isTemporaryGroup() { fail(); return false; } @Override public int getId() { fail(); return 0; } private void fail() throws IllegalStateException { throw new IllegalStateException("Use of null storage group"); } }; // this is the ONLY place we should be calling mapping from public static WorkerGroup.MappingSolution mapKeyForInsert(ExecutionState estate, StorageGroup wg, IKeyValue inkey) throws PEException { return inkey.getDistributionModel().mapKeyForInsert(estate.getCatalogDAO(), wg, bindKey(estate,inkey)); } private static IKeyValue bindKey(ExecutionState estate, IKeyValue ikv) throws PEException { return ikv.rebind(estate.getValues()); } public static WorkerGroup.MappingSolution mapKeyForUpdate(ExecutionState estate, StorageGroup wg, IKeyValue inkey) throws PEException { return inkey.getDistributionModel().mapKeyForUpdate(estate.getCatalogDAO(), wg, bindKey(estate,inkey)); } public static WorkerGroup.MappingSolution mapKeyForQuery(ExecutionState estate, StorageGroup wg, IKeyValue key, DistKeyOpType operation) throws PEException { return key.getDistributionModel().mapKeyForQuery( estate.getCatalogDAO(), wg, bindKey(estate,key), operation); } public static WorkerGroup.MappingSolution mapKeyForQuery(ExecutionState estate, StorageGroup wg, IKeyValue key, DistributionModel givenDistribution, DistKeyOpType operation) throws PEException { return givenDistribution.mapKeyForQuery( estate.getCatalogDAO(), wg, bindKey(estate,key), operation); } }