/* This file is part of VoltDB.
* Copyright (C) 2008-2012 VoltDB Inc.
*
* 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.sysprocs;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.voltdb.BackendTarget;
import org.voltdb.DependencySet;
import org.voltdb.ParameterSet;
import org.voltdb.ProcInfo;
import org.voltdb.VoltSystemProcedure;
import org.voltdb.VoltTable;
import org.voltdb.jni.ExecutionEngine;
import edu.brown.hstore.HStoreConstants;
import edu.brown.hstore.PartitionExecutor;
import edu.brown.hstore.PartitionExecutor.SystemProcedureExecutionContext;
import edu.brown.hstore.txns.AbstractTransaction;
import edu.brown.logging.LoggerUtil;
import edu.brown.logging.LoggerUtil.LoggerBoolean;
/**
* Execute a user-provided SQL statement. This code coordinates the execution of
* the plan fragments generated by the embedded planner process.
*/
@ProcInfo(singlePartition = false)
public class AdHoc extends VoltSystemProcedure {
private static final Logger LOG = Logger.getLogger(AdHoc.class);
private static final LoggerBoolean debug = new LoggerBoolean();
static {
LoggerUtil.attachObserver(LOG, debug);
}
final int AGG_DEPID = 1;
final int COLLECT_DEPID = 2 | HStoreConstants.MULTIPARTITION_DEPENDENCY;
@Override
public void initImpl() {
this.registerPlanFragment(SysProcFragmentId.PF_runAdHocFragment);
}
@Override
public DependencySet executePlanFragment(Long txn_id, Map<Integer, List<VoltTable>> dependencies, int fragmentId, ParameterSet params, SystemProcedureExecutionContext context) {
// get the three params (depId, json plan, sql stmt)
int outputDepId = (Integer) params.toArray()[0];
String plan = (String) params.toArray()[1];
String sql = (String) params.toArray()[2];
int inputDepId = -1;
// make dependency ids available to the execution engine
if ((dependencies != null) && (dependencies.size() > 00)) {
assert(dependencies.size() <= 1);
for (int x : dependencies.keySet()) {
inputDepId = x; break;
}
context.getExecutionEngine().stashWorkUnitDependencies(dependencies);
}
VoltTable table = null;
if (executor.getBackendTarget() == BackendTarget.HSQLDB_BACKEND) {
// Call HSQLDB
assert(sql != null);
// table = m_hsql.runDML(sql);
}
else {
assert(plan != null);
ExecutionEngine ee = context.getExecutionEngine();
AbstractTransaction ts = this.hstore_site.getTransaction(txn_id);
// Enable read/write set tracking
if (hstore_conf.site.exec_readwrite_tracking && ts.hasExecutedWork(this.partitionId) == false) {
if (debug.val)
LOG.trace(String.format("%s - Enabling read/write set tracking in EE at partition %d",
ts, this.partitionId));
ee.trackingEnable(txn_id);
}
// Always mark this information for the txn so that we can
// rollback anything that it may do
ts.markExecNotReadOnly(this.partitionId);
ts.markExecutedWork(this.partitionId);
table = ee.executeCustomPlanFragment(plan, outputDepId, inputDepId, txn_id,
context.getLastCommittedTxnId(),
ts.getLastUndoToken(this.partitionId));
}
return new DependencySet(new int[]{ outputDepId }, new VoltTable[]{ table });
}
/**
* Parameters to the run method are created internally and do not match
* the input the user passes to {@link org.voltdb.client.Client#callProcedure}.
* The user passes a single parameter, the SQL statement to compile and
* execute.
*
* @param ctx Internal.
* @param aggregatorFragment Internal.
* @param collectorFragment Internal.
* @param sql User provided SQL statement.
* @param isReplicatedTableDML Internal.
* @return The result of the user's query. If the user's SQL statement was
* a DML query, a table with a single untitled column is returned containing
* a single {@link org.voltdb.VoltType#BIGINT} row value: the number of tuples
* affected. This DML output matches the usual DML result from a VoltDB stored
* procedure.
*/
public VoltTable[] run(String aggregatorFragment, String collectorFragment,
String sql, int isReplicatedTableDML) {
boolean replicatedTableDML = isReplicatedTableDML == 1;
SynthesizedPlanFragment[] pfs = null;
VoltTable[] results = null;
ParameterSet params = null;
if (executor.getBackendTarget() == BackendTarget.HSQLDB_BACKEND) {
pfs = new SynthesizedPlanFragment[1];
// JUST SEND ONE FRAGMENT TO HSQL, IT'LL IGNORE EVERYTHING BUT SQL AND DEPID
pfs[0] = new SynthesizedPlanFragment();
pfs[0].fragmentId = SysProcFragmentId.PF_runAdHocFragment;
pfs[0].outputDependencyIds = new int[]{ AGG_DEPID };
pfs[0].multipartition = false;
params = new ParameterSet();
params.setParameters(AGG_DEPID, "", sql);
pfs[0].parameters = params;
}
else {
pfs = new SynthesizedPlanFragment[2];
if (collectorFragment != null) {
pfs = new SynthesizedPlanFragment[2];
// COLLECTION FRAGMENT NEEDS TO RUN FIRST
pfs[1] = new SynthesizedPlanFragment();
pfs[1].fragmentId = SysProcFragmentId.PF_runAdHocFragment;
pfs[1].outputDependencyIds = new int[]{ COLLECT_DEPID };
pfs[1].multipartition = true;
params = new ParameterSet();
params.setParameters(COLLECT_DEPID, collectorFragment, sql);
pfs[1].parameters = params;
}
else {
pfs = new SynthesizedPlanFragment[1];
}
// AGGREGATION FRAGMENT DEPENDS ON THE COLLECTION FRAGMENT
pfs[0] = new SynthesizedPlanFragment();
pfs[0].fragmentId = SysProcFragmentId.PF_runAdHocFragment;
pfs[0].outputDependencyIds = new int[]{ AGG_DEPID };
if (collectorFragment != null)
pfs[0].inputDependencyIds = new int[] { COLLECT_DEPID };
pfs[0].multipartition = false;
params = new ParameterSet();
params.setParameters(AGG_DEPID, aggregatorFragment, sql);
pfs[0].parameters = params;
}
// distribute and execute these fragments providing pfs and id of the
// aggregator's output dependency table.
results =
executeSysProcPlanFragments(pfs, AGG_DEPID);
// rather icky hack to handle how the number of modified tuples will always be
// inflated when changing replicated tables - the user really doesn't want to know
// the big number, just the small one
// PAVLO: 2013-07-07
// This hack is no longer needed because we will aggregate the # of modified
// tuples in the EE correctly.
// if (replicatedTableDML) {
// assert(results.length == 1);
// long changedTuples = results[0].asScalarLong();
// // int num_partitions = catalogContext.numberOfPartitions;
// // assert((changedTuples % num_partitions) == 0);
//
// VoltTable retval = new VoltTable(new VoltTable.ColumnInfo("", VoltType.BIGINT));
//// retval.addRow(changedTuples / num_partitions);
// retval.addRow(changedTuples);
// results[0] = retval;
// }
return results;
}
}