package com.tesora.dve.sql.transform.execution;
/*
* #%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.Iterator;
import java.util.List;
import com.tesora.dve.db.DBNative;
import org.apache.commons.lang.StringUtils;
import com.tesora.dve.common.catalog.HasAutoIncrementTracker;
import com.tesora.dve.common.catalog.StorageGroup;
import com.tesora.dve.common.catalog.UserColumn;
import com.tesora.dve.common.catalog.UserTable;
import com.tesora.dve.db.Emitter.EmitOptions;
import com.tesora.dve.db.GenericSQLCommand;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.queryplan.QueryStepMultiTupleRedistOperation;
import com.tesora.dve.queryplan.QueryStepOperation;
import com.tesora.dve.queryplan.TableHints;
import com.tesora.dve.queryplan.TempTableDeclHints;
import com.tesora.dve.queryplan.TempTableGenerator;
import com.tesora.dve.resultset.ProjectionInfo;
import com.tesora.dve.resultset.ResultRow;
import com.tesora.dve.server.global.HostService;
import com.tesora.dve.server.messaging.SQLCommand;
import com.tesora.dve.singleton.Singletons;
import com.tesora.dve.sql.node.expression.ExpressionNode;
import com.tesora.dve.sql.schema.ConnectionValues;
import com.tesora.dve.sql.schema.Database;
import com.tesora.dve.sql.schema.DistributionKey;
import com.tesora.dve.sql.schema.DistributionKeyTemplate;
import com.tesora.dve.sql.schema.DistributionVector;
import com.tesora.dve.sql.schema.DistributionVector.Model;
import com.tesora.dve.sql.schema.ExplainOptions;
import com.tesora.dve.sql.schema.PEColumn;
import com.tesora.dve.sql.schema.PEStorageGroup;
import com.tesora.dve.sql.schema.PETable;
import com.tesora.dve.sql.schema.SchemaContext;
import com.tesora.dve.sql.schema.TempTable;
import com.tesora.dve.sql.schema.mt.TableScope;
import com.tesora.dve.sql.statement.dml.ProjectingStatement;
import com.tesora.dve.sql.util.Functional;
import com.tesora.dve.sql.util.Pair;
public final class RedistributionExecutionStep extends
AbstractProjectingExecutionStep {
// for redists
protected PETable targetTable = null;
// if redist, the dv
protected DistributionKeyTemplate distKey = null;
// if redist, the target persistent group
protected PEStorageGroup targetGroup = null;
// hints for the temp table declaration
protected TempTableDeclHints declarationHints = null;
protected PEColumn missingAutoInc;
protected Integer offsetOfExistingAutoinc;
// any on dup key clause from the original
protected SQLCommand redistOnDupKey;
// if redisting to a tenant table, the tenant scope
protected TableScope targetScope;
boolean enforceScalarValue = false;
boolean insertIgnore = false;
boolean userlandTemporaryTable = false;
// any table generator
protected TempTableGenerator generator;
public static RedistributionExecutionStep build(SchemaContext sc, Database<?> db, PEStorageGroup srcGroup, DistributionVector sourceDV,
ProjectingStatement sql, PEStorageGroup targetGroup, PETable redistToTable,
TableScope redistToScopedTable,
DistributionKeyTemplate dv,
PEColumn missingAutoInc,
Integer offsetToExistingAutoInc,
List<ExpressionNode> onDupKey,
Boolean rc, DistributionKey dk,
boolean mustEnforceScalarValue,
boolean insertIgnore,
TempTableGenerator tempTableGenerator,
DMLExplainRecord splain) throws PEException {
maybeApplyMultitenant(sc,sql);
return new RedistributionExecutionStep(sc, db, srcGroup, sourceDV, sql, targetGroup, redistToTable,
redistToScopedTable, dv, missingAutoInc, offsetToExistingAutoInc,
onDupKey, rc, dk, mustEnforceScalarValue, insertIgnore, tempTableGenerator, splain);
}
public static RedistributionExecutionStep build(SchemaContext sc, Database<?> db, PEStorageGroup storageGroup, String sql, DistributionVector sourceVect,
PETable redistToTable,
PEStorageGroup targetGroup,
DistributionKeyTemplate distKeyTemplate,
DMLExplainRecord splain) throws PEException {
if (redistToTable != null)
redistToTable.setFrozen();
return new RedistributionExecutionStep(sc, db, storageGroup, sql, sourceVect, redistToTable, targetGroup, distKeyTemplate, splain);
}
private RedistributionExecutionStep(SchemaContext sc, Database<?> db, PEStorageGroup srcGroup,
DistributionVector sourceDV, ProjectingStatement sql, PEStorageGroup targetGroup, PETable redistToTable,
TableScope redistToScopedTable,
DistributionKeyTemplate dv,
PEColumn missingAutoInc,
Integer offsetOfExistingAutoInc,
List<ExpressionNode> onDupKey,
Boolean rc,
DistributionKey dk, boolean mustEnforceScalarValue,
boolean insertIgnore,
TempTableGenerator tempTableGenerator,
DMLExplainRecord splain) throws PEException {
super(db, srcGroup, sourceDV, dk, sql.getGenericSQL(sc, false, true), splain);
effectiveType = sql.getExecutionType();
targetTable = redistToTable;
distKey = dv;
this.targetGroup = targetGroup;
if ((redistToTable != null) && redistToTable.isTempTable()) {
this.declarationHints = ((TempTable) redistToTable).finalizeHints(sc);
}
if (targetTable.isUserlandTemporaryTable() ||
sql.getDerivedInfo().hasUserlandTemporaryTables())
userlandTemporaryTable = true;
this.missingAutoInc = missingAutoInc;
this.offsetOfExistingAutoinc = offsetOfExistingAutoInc;
useRowCount = rc;
targetScope = redistToScopedTable;
requiresReferenceTimestamp = sql.getDerivedInfo().doSetTimestampVariable();
if (onDupKey != null && !onDupKey.isEmpty()) {
StringBuilder buf = new StringBuilder();
Singletons.require(DBNative.class).getEmitter().emitInsertSuffix(sc, sc.getValues(), onDupKey, buf);
// should think about moving this into schedule
redistOnDupKey = new SQLCommand(sc, buf.toString());
}
this.enforceScalarValue = mustEnforceScalarValue;
this.insertIgnore = insertIgnore;
this.generator = tempTableGenerator;
}
private RedistributionExecutionStep(SchemaContext sc, Database<?> db, PEStorageGroup storageGroup, String sql, DistributionVector sourceVect,
PETable redistToTable,
PEStorageGroup targetGroup,
DistributionKeyTemplate distKeyTemplate,
DMLExplainRecord splain) throws PEException {
super(db, storageGroup, sourceVect, null, new GenericSQLCommand(sc, sql), splain);
targetTable = redistToTable;
this.targetGroup = targetGroup;
distKey = distKeyTemplate;
if ((redistToTable != null) && redistToTable.isTempTable()) {
this.declarationHints = ((TempTable) redistToTable).finalizeHints(sc);
}
}
@Override
public void schedule(ExecutionPlanOptions opts, List<QueryStepOperation> qsteps,
ProjectionInfo projection, SchemaContext sc,
ConnectionValuesMap cvm, ExecutionPlan containing) throws PEException {
QueryStepMultiTupleRedistOperation qsrdo = null;
ConnectionValues cv = cvm.getValues(containing);
StorageGroup sg = getStorageGroup(sc,cv);
if (targetTable.mustBeCreated()) {
qsrdo = new QueryStepMultiTupleRedistOperation(sg, getPersistentDatabase(), getCommand(sc,cv), getDistributionModel(sc));
if (targetTable.isExplicitlyDeclared()) {
// need to create a new context to avoid leaking
SchemaContext mutableContext = SchemaContext.makeMutableIndependentContext(sc);
mutableContext.setValues(sc.getValues());
mutableContext.beginSaveContext();
try {
UserTable ut = targetTable.getPersistent(mutableContext);
qsrdo.toUserTable(targetGroup.getPersistent(sc,cv), ut, declarationHints, true);
} finally {
mutableContext.endSaveContext();
}
} else {
final Database<?> targetDb = targetTable.getDatabase(sc);
qsrdo.toTempTable(targetGroup.getPersistent(sc,cv), (targetDb != null) ? targetDb : getPersistentDatabase(), targetTable.getName(sc,cv).get(), true);
}
if (distKey.usesColumns(sc)) {
if (Model.RANGE.equals(distKey.getModel(sc)))
qsrdo.distributeOn(distKey.getColumnNames(), distKey.getTable().asTable().getPersistentTable(sc));
else
qsrdo.distributeOn(distKey.getColumnNames());
}
qsrdo.withTempHints(declarationHints);
} else {
TableHints hints = new TableHints();
sc.beginSaveContext();
try {
HasAutoIncrementTracker hait = null;
if (missingAutoInc != null || offsetOfExistingAutoinc != null) {
if (targetScope != null)
hait = targetScope.persistTree(sc);
else
hait = targetTable.persistTree(sc);
}
if (missingAutoInc != null) {
hints.withMissingAutoIncs(new Pair<UserColumn,HasAutoIncrementTracker>(missingAutoInc.persistTree(sc), hait));
}
if (offsetOfExistingAutoinc != null) {
hints.withExistingAutoIncs(new Pair<Integer, HasAutoIncrementTracker>(offsetOfExistingAutoinc, hait));
}
qsrdo = new QueryStepMultiTupleRedistOperation(sg, getPersistentDatabase(), getCommand(sc,cv), getDistributionModel(sc))
.toUserTable(targetTable.getPersistentStorage(sc).getPersistent(sc), targetTable.getPersistentTable(sc), hints, true);
} finally {
sc.endSaveContext();
}
if (redistOnDupKey != null)
qsrdo.onDupKey(redistOnDupKey);
}
DistributionKey dk = getDistributionKey();
if (dk != null)
qsrdo.setSpecifiedDistKeyValue(dk.getDetachedKey(sc,cv));
qsrdo.setEnforceScalarValue(enforceScalarValue);
qsrdo.setInsertIgnore(insertIgnore);
if (generator != null)
qsrdo.withTableGenerator(generator);
if (userlandTemporaryTable)
qsrdo.withUserlandTemporaryTables();
qsrdo.setStatistics(getStepStatistics(sc));
qsteps.add(qsrdo);
}
public PEStorageGroup getTargetGroup(SchemaContext sc, ConnectionValues cv) {
if (targetGroup == null) return null;
return targetGroup.getPEStorageGroup(sc,cv);
}
public PETable getTargetTable() {
return targetTable;
}
public String getRedistTable(SchemaContext sc, ConnectionValues cv) {
if (targetTable == null) return null;
return targetTable.getName(sc,cv).get();
}
public DistributionKeyTemplate getDistKey() {
return distKey;
}
public String getTargetDistributionModel(SchemaContext sc) {
if (targetTable == null) return null;
return distKey.getModel(sc).getPersistentName();
}
@Override
protected String explainStepType() {
return "REDISTRIBUTE";
}
@Override
public void display(SchemaContext sc, ConnectionValuesMap cvm, ExecutionPlan containing, List<String> buf, String indent, EmitOptions opts) {
super.display(sc, cvm,containing,buf, indent, opts);
ConnectionValues cv = cvm.getValues(containing);
buf.add(indent + " redist to (" + targetTable.getName(sc,cv).get() + ") using model " + describeTargetModel(sc,cv) + " on " + targetGroup.getPEStorageGroup(sc,cv).getPersistent(sc,cv));
if (declarationHints != null) {
List<List<String>> indices = declarationHints.getIndexes();
if (!indices.isEmpty()) {
StringBuilder ibuf = new StringBuilder();
ibuf.append("indexes ");
boolean first = true;
for (List<String> s : indices) {
if (first) first = false;
else ibuf.append(",");
ibuf.append("(");
ibuf.append(StringUtils.join(s, ','));
ibuf.append(")");
}
buf.add(indent + " " + ibuf.toString());
}
}
}
private String describeTargetModel(SchemaContext sc, ConnectionValues cv) {
if (distKey == null) return targetTable.getDistributionVector(sc).describe(sc, cv);
return distKey.describe(sc);
}
@Override
protected void addStepExplainColumns(SchemaContext sc, ConnectionValues cv, ResultRow rr, ExplainOptions opts) {
super.addStepExplainColumns(sc, cv, rr, opts);
addStringResult(rr,explainTargetGroup(sc,cv));
addStringResult(rr,explainTargetTable(sc, cv));
addStringResult(rr,explainTargetDist(sc));
addStringResult(rr,explainTargetHints(sc));
addStringResult(rr,explainExplainHint(sc));
}
protected String explainTargetGroup(SchemaContext sc,ConnectionValues cv) {
if (targetGroup == null) return null;
return explainStorageGroup(sc,targetGroup,cv);
}
protected String explainTargetTable(SchemaContext sc, ConnectionValues cv) {
if (targetTable == null) return null;
return targetTable.getName(sc,cv).get();
}
protected String explainTargetDist(SchemaContext sc) {
StringBuilder buf = new StringBuilder();
String model = (targetGroup != null ? getTargetDistributionModel(sc) : null);
String vect = null;
if (targetGroup != null && distKey != null)
vect = Functional.join(distKey.getColumnNames(), ",");
if (model != null)
buf.append(model).append(" distribute");
if (vect != null && !"".equals(vect.trim()))
buf.append(" on ").append(vect);
return buf.toString();
}
protected String explainTargetHints(SchemaContext sc) {
if (declarationHints == null) {
return null;
}
List<List<String>> indices = declarationHints.getIndexes();
List<List<String>> uniques = declarationHints.getUniqueKeys();
if (indices == null && uniques == null) {
return null;
}
StringBuilder buf = new StringBuilder();
if (uniques != null && !uniques.isEmpty())
explainIndexHints("u",uniques,buf);
if (indices != null && !indices.isEmpty())
explainIndexHints("i",indices,buf);
return buf.toString();
}
private void explainIndexHints(String prefix, List<List<String>> cols, StringBuilder buf) {
buf.append(prefix).append(":");
for(Iterator<List<String>> iter = cols.iterator(); iter.hasNext();) {
buf.append(iter.next());
if (iter.hasNext())
buf.append(",");
}
}
@Override
public void prepareForCache() {
if (targetTable != null)
targetTable.setFrozen();
super.prepareForCache();
}
}