package com.tesora.dve.sql.transform.strategy;
/*
* #%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.Collections;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.tesora.dve.common.catalog.ConstraintType;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.sql.ParserException.Pass;
import com.tesora.dve.sql.SchemaException;
import com.tesora.dve.sql.expression.ColumnKey;
import com.tesora.dve.sql.expression.ExpressionUtils;
import com.tesora.dve.sql.expression.TableKey;
import com.tesora.dve.sql.jg.JoinGraph;
import com.tesora.dve.sql.node.expression.ColumnInstance;
import com.tesora.dve.sql.node.expression.ConstantExpression;
import com.tesora.dve.sql.node.expression.ExpressionAlias;
import com.tesora.dve.sql.node.expression.ExpressionNode;
import com.tesora.dve.sql.node.expression.FunctionCall;
import com.tesora.dve.sql.node.expression.NameAlias;
import com.tesora.dve.sql.node.expression.TableInstance;
import com.tesora.dve.sql.node.expression.TempTableInstance;
import com.tesora.dve.sql.node.structural.FromTableReference;
import com.tesora.dve.sql.node.structural.JoinSpecification;
import com.tesora.dve.sql.node.structural.JoinedTable;
import com.tesora.dve.sql.node.test.EngineConstant;
import com.tesora.dve.sql.schema.Column;
import com.tesora.dve.sql.schema.DistributionVector;
import com.tesora.dve.sql.schema.DistributionVector.Model;
import com.tesora.dve.sql.schema.FunctionName;
import com.tesora.dve.sql.schema.PEAbstractTable;
import com.tesora.dve.sql.schema.PEColumn;
import com.tesora.dve.sql.schema.PEForeignKey;
import com.tesora.dve.sql.schema.PEKey;
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.TempTableCreateOptions;
import com.tesora.dve.sql.statement.dml.AliasInformation;
import com.tesora.dve.sql.statement.dml.DMLStatement;
import com.tesora.dve.sql.statement.dml.DeleteStatement;
import com.tesora.dve.sql.statement.dml.SelectStatement;
import com.tesora.dve.sql.statement.dml.UpdateStatement;
import com.tesora.dve.sql.transform.ColumnInstanceCollector;
import com.tesora.dve.sql.transform.CopyVisitor;
import com.tesora.dve.sql.transform.SchemaMapper;
import com.tesora.dve.sql.transform.behaviors.defaults.DefaultFeaturePlannerFilter;
import com.tesora.dve.sql.transform.execution.ExecutionSequence;
import com.tesora.dve.sql.transform.execution.FilterExecutionStep.LateBindingOperationFilter;
import com.tesora.dve.sql.transform.execution.LateBindingUpdateCountFilter.LateBindingUpdateCounter;
import com.tesora.dve.sql.transform.strategy.featureplan.FeatureStep;
import com.tesora.dve.sql.transform.strategy.featureplan.MultiFeatureStep;
import com.tesora.dve.sql.transform.strategy.featureplan.ProjectingFeatureStep;
import com.tesora.dve.sql.transform.strategy.featureplan.RedistFeatureStep;
import com.tesora.dve.sql.transform.strategy.featureplan.RedistributionFlags;
import com.tesora.dve.sql.util.ListOfPairs;
import com.tesora.dve.sql.util.ListSet;
import com.tesora.dve.sql.util.Pair;
/**
* Plans for UPDATE [IGNORE] which affect distribution vector columns,
* otherwise push down to the persistent sites.
*
* UPDATE:
* convert the update to a select
* for updated columns, in the select use the rhs
* for nonupdated columns, just reference
* convert the update to a delete
*
* [1] select, redist to temp
* [2] delete
* [3] select temp, redist to table
*
* UPDATE IGNORE:
* [1] select all matching rows and redist to temp
* [2] execute the update statement on the temp site
* [3] delete the matching rows from the parent
* [4] redist back to the parent
*
* Of course, if the update doesn't touch unique columns or dist key columns, we can push it down
* unless the update is not colocated. In that case, we must build a lookup table prior to pushing it down.
* The plan then is
* [1] convert the update to a select. put any dependent expressions on the projection, along with the uk columns
* of the updated table.
* [2] redist the select back onto the pers group
* [3] execute the update using the lookup table.
*/
public class UpdateRewriteTransformFactory extends TransformFactory {
enum Variety {
UNIQUE,
DIST,
COLOCATION,
NESTED
}
private static void validateUpdateTableInstance(final PEAbstractTable<?> updateTable) {
if (updateTable.isView()) {
throw new SchemaException(Pass.PLANNER, "No support for updating views");
}
}
private static Set<PEKey> getUniqueKeys(final SchemaContext sc, final PETable table) {
final Set<PEKey> uniqueKeys = new HashSet<PEKey>();
uniqueKeys.addAll(table.getUniqueKeys(sc));
uniqueKeys.add(table.getPrimaryKey(sc));
return uniqueKeys;
}
private static Set<PEKey> getKeysWithColumn(final Set<PEKey> keys, final PEColumn column) {
final Set<PEKey> keysWithColumn = new HashSet<PEKey>();
for (final PEKey key : keys) {
if (key.containsColumn(column)) {
keysWithColumn.add(key);
}
}
return keysWithColumn;
}
private static List<Integer> keyColumnsToOffsets(final SchemaContext sc, final PEKey key) {
final List<PEColumn> keyColumns = key.getColumns(sc);
final List<Integer> keyColumnOffsets = new ArrayList<Integer>(keyColumns.size());
for (final PEColumn keyColumn : keyColumns) {
keyColumnOffsets.add(keyColumn.getPosition());
}
return keyColumnOffsets;
}
private static List<PEColumn> getColumnsAtOffsets(final List<PEColumn> columns, final List<Integer> offsets) {
final List<PEColumn> colsAtOffsets = new ArrayList<PEColumn>(offsets.size());
for (final Integer i : offsets) {
colsAtOffsets.add(columns.get(i));
}
return colsAtOffsets;
}
public static ListOfPairs<ColumnKey, ExpressionNode> getUpdateExpressions(UpdateStatement us) throws PEException {
ListOfPairs<ColumnKey, ExpressionNode> out = new ListOfPairs<ColumnKey, ExpressionNode>();
for (ExpressionNode en : us.getUpdateExpressionsEdge().getMulti()) {
if (!EngineConstant.FUNCTION.has(en, EngineConstant.EQUALS))
throw new PEException("Malformed update statement: " + en + " is not a set expression");
FunctionCall fc = (FunctionCall) en;
if (fc.getParametersEdge().get(0) instanceof ColumnInstance) {
out.add(((ColumnInstance)fc.getParametersEdge().get(0)).getColumnKey(), fc.getParametersEdge().get(1));
} else {
throw new PEException("Malformed update statement: set expression " + en + " lhs is not a column");
}
}
return out;
}
public static TableKey getUpdateTables(final ListOfPairs<ColumnKey, ExpressionNode> updateExprs) {
final ListSet<TableKey> updatedTables = new ListSet<TableKey>();
for (final Pair<ColumnKey, ExpressionNode> ue : updateExprs) {
updatedTables.add(ue.getFirst().getTableKey());
}
if (updatedTables.size() != 1) {
throw new SchemaException(Pass.PLANNER, "Unsupported: multiple update tables");
}
return updatedTables.get(0);
}
private static ExpressionNode rebuildOrDecomposedWhereClause(final ListSet<ExpressionNode> whereClauseParts) {
if (whereClauseParts.isEmpty()) {
return null;
}
return ExpressionUtils.safeBuildOr(whereClauseParts);
}
private static List<Column<?>> getDistributionVectorColumns(final SchemaContext sc, final PETable table) {
final DistributionVector dv = table.getDistributionVector(sc);
if (dv.usesColumns(sc)) {
return new ArrayList<Column<?>>(dv.getColumns(sc));
}
final PEKey primary = table.getPrimaryKey(sc);
if (primary != null) {
return new ArrayList<Column<?>>(primary.getColumns(sc));
}
return new ArrayList<Column<?>>(table.getColumns(sc));
}
private static final List<Integer> getColumnOffsets(final List<Column<?>> columns) {
final List<Integer> offsets = new ArrayList<Integer>(columns.size());
for (final Column<?> column : columns) {
offsets.add(column.getPosition());
}
return offsets;
}
@Override
public FeaturePlannerIdentifier getFeaturePlannerID() {
return FeaturePlannerIdentifier.UPDATE_DIST_VECT;
}
private static class RHSUpdateState {
private int offset;
private ConstantExpression constant;
public RHSUpdateState(int offset) {
this.offset = offset;
this.constant = null;
}
public RHSUpdateState(ConstantExpression ce) {
this.offset = -1;
this.constant = ce;
}
public ExpressionNode getExpression(SelectStatement tempTableSelect) {
if (this.offset > -1)
return (ExpressionNode) ExpressionUtils.getTarget(tempTableSelect.getProjectionEdge().get(offset)).copy(null);
else
return (ExpressionNode) constant.copy(null);
}
public int getOffset() {
return offset;
}
public ConstantExpression getConstant() {
return constant;
}
}
@Override
public FeatureStep plan(DMLStatement stmt, PlannerContext context)
throws PEException {
final UpdateStatement us = (UpdateStatement) stmt;
final SchemaContext sc = context.getContext();
PEForeignKey.doForeignKeyChecks(sc, us);
// figure out which variety of update we have
// our search order is:
// unique key update, dist key update, partitions, nesting
ListSet<TableKey> updatedTables = new ListSet<TableKey>();
EnumSet<Variety> flags = EnumSet.noneOf(Variety.class);
for(Pair<ColumnKey,ExpressionNode> p : getUpdateExpressions(us)) {
updatedTables.add(p.getFirst().getTableKey());
PEColumn pec = p.getFirst().getPEColumn();
if (pec.isUniquePart() || pec.isPrimaryKeyPart())
flags.add(Variety.UNIQUE);
if (pec.isPartOfDistributionVector())
flags.add(Variety.DIST);
}
if (updatedTables.get(0).getAbstractTable().isView())
throw new SchemaException(Pass.PLANNER, "No support for updatable views");
if (!updatedTables.get(0).getAbstractTable().getDistributionVector(sc).usesColumns(sc)) {
flags.remove(Variety.DIST);
flags.remove(Variety.UNIQUE);
}
if (flags.isEmpty()) {
PEKey uk = updatedTables.get(0).getAbstractTable().asTable().getUniqueKey(sc);
if (EngineConstant.NESTED.hasValue(us,sc)) {
flags.add(uk == null ? Variety.DIST : Variety.NESTED);
} else {
JoinGraph jg = EngineConstant.PARTITIONS.getValue(us, sc);
if (jg.requiresRedistribution()) {
flags.add(uk == null ? Variety.DIST : Variety.COLOCATION);
}
}
}
if (flags.isEmpty())
return null;
if (updatedTables.size() > 1)
throw new SchemaException(Pass.PLANNER, "Unsupported: update on multiple tables");
PlannerContext childContext = context.withTransform(getFeaturePlannerID());
if (flags.contains(Variety.DIST) || flags.contains(Variety.UNIQUE))
return planComplexUpdate(childContext,us);
else
return planSimpleUpdate(childContext,us);
}
private FeatureStep planComplexUpdate(PlannerContext pc, final UpdateStatement us) throws PEException {
UpdateStatement updateStmtCopy = CopyVisitor.copy(us);
SchemaContext sc = pc.getContext();
final ListOfPairs<ColumnKey, ExpressionNode> updateExpressions = getUpdateExpressions(updateStmtCopy);
final TableKey btk = getUpdateTables(updateExpressions);
final TableInstance bti = btk.toInstance();
final PEAbstractTable<?> bta = bti.getAbstractTable();
validateUpdateTableInstance(bta);
final PETable updateTable = bta.asTable();
final Set<PEKey> uniqueKeys = getUniqueKeys(sc, updateTable);
final Map<Integer, ExpressionNode> updateExprsByColumnOffset = new HashMap<Integer, ExpressionNode>(updateExpressions.size());
final Set<List<Integer>> uniqueKeyColumnOffsets = new HashSet<List<Integer>>(uniqueKeys.size());
final ListSet<ExpressionNode> whereClauseParts = ExpressionUtils.decomposeOrClause(updateStmtCopy.getWhereClause());
for (final Pair<ColumnKey, ExpressionNode> ue : updateExpressions) {
final PEColumn expressionColumn = ue.getFirst().getPEColumn();
final ExpressionNode updateExpression = ue.getSecond();
final Integer columnOffset = expressionColumn.getPosition();
updateExprsByColumnOffset.put(columnOffset, updateExpression);
if (expressionColumn.isUniquePart() || expressionColumn.isPrimaryKeyPart()) {
whereClauseParts.add(new FunctionCall(FunctionName.makeEquals(), (ExpressionNode)ue.getFirst().toInstance().copy(null), updateExpression));
final Set<PEKey> columnKeys = getKeysWithColumn(uniqueKeys, expressionColumn);
for (final PEKey key : columnKeys) {
uniqueKeyColumnOffsets.add(keyColumnsToOffsets(sc, key));
}
}
}
final ExpressionNode whereClause = rebuildOrDecomposedWhereClause(whereClauseParts);
/*
* Here we build a redistribution select with
* correct column references and mappings for
* all required columns.
*/
updateStmtCopy = CopyVisitor.copy(us);
ListSet<ExpressionNode> projs = new ListSet<ExpressionNode>();
Set<ColumnKey> projsColKeys = new HashSet<ColumnKey>();
// we need all of the columns on the base table + all columns that are needed for rhs exprs
for(PEColumn c : updateTable.getColumns(sc)) {
ColumnInstance ci = new ColumnInstance(c,btk.toInstance());
projs.add(ci);
projsColKeys.add(new ColumnKey(ci));
}
// add in any columns which are needed by the update exprs which aren't in the update table
ListOfPairs<Integer,RHSUpdateState> updateOffsets = new ListOfPairs<Integer,RHSUpdateState>();
for (final Pair<ColumnKey, ExpressionNode> uex : getUpdateExpressions(updateStmtCopy)) {
if (EngineConstant.CONSTANT.has(uex.getSecond())) {
updateOffsets.add(uex.getFirst().getPEColumn().getPosition(), new RHSUpdateState((ConstantExpression)uex.getSecond()));
} else {
updateOffsets.add(uex.getFirst().getPEColumn().getPosition(), new RHSUpdateState(projs.size()));
projs.add(uex.getSecond());
projsColKeys.add(uex.getFirst());
}
}
for (ColumnInstance ci : ColumnInstanceCollector.getColumnInstances(updateStmtCopy.getWhereClause())) {
ColumnKey ck = new ColumnKey(ci);
if (!projsColKeys.contains(ck))
projs.add(ci);
}
SelectStatement select = new SelectStatement(new AliasInformation())
.setTables(updateStmtCopy.getTables())
.setProjection(projs)
.setWhereClause(updateStmtCopy.getWhereClause());
select.setOrderBy(updateStmtCopy.getOrderBys());
select.setLimit(updateStmtCopy.getLimit());
select.getDerivedInfo().take(updateStmtCopy.getDerivedInfo());
SchemaMapper mapper = new SchemaMapper(updateStmtCopy.getMapper().getOriginals(), select, updateStmtCopy.getMapper().getCopyContext());
select.setMapper(mapper);
select.setWhereClause(whereClause);
select.setLocking();
final SelectStatement redistStmtCopy = CopyVisitor.copy(select);
final DeleteStatement delete = new DeleteStatement().setTruncate(false);
delete.setTables(redistStmtCopy.getTables()).setWhereClause(redistStmtCopy.getWhereClause());
delete.getDerivedInfo().copyTake(redistStmtCopy.getDerivedInfo());
delete.getDerivedInfo().addNestedStatements(redistStmtCopy.getDerivedInfo().getNestedQueries(redistStmtCopy.getTables()));
delete.getDerivedInfo().addNestedStatements(redistStmtCopy.getDerivedInfo().getNestedQueries(redistStmtCopy.getWhereClause()));
delete.setTargetDeletes(Collections.singletonList(btk.toInstance()));
if (emitting()) {
emit(select.getSQL(sc, ""));
emit(delete.getSQL(sc, ""));
}
ProjectingFeatureStep selectStep =
(ProjectingFeatureStep) buildPlan(select,pc,DefaultFeaturePlannerFilter.INSTANCE);
FeatureStep deleteStep =
buildPlan(delete,pc,DefaultFeaturePlannerFilter.INSTANCE);
final PEStorageGroup pesg = pc.getTempGroupManager().getGroup(true);
final List<Integer> distVect = getColumnOffsets(getDistributionVectorColumns(sc, updateTable));
// Build a temporary table containing updated rows from the target
// table + all rows with potential uniqueness conflicts. All records
// have to be available on a single site.
RedistFeatureStep tempTableStep =
selectStep.redist(pc, this,
new TempTableCreateOptions(Model.BROADCAST, pesg)
.distributeOn(distVect),
null,
null);
final TempTable tt = tempTableStep.getTargetTempTable();
final List<PEColumn> tempTableColumns = tt.getColumns(sc);
for (final List<Integer> offsets : uniqueKeyColumnOffsets) {
tt.addConstraint(sc, ConstraintType.UNIQUE, getColumnsAtOffsets(tempTableColumns, offsets));
}
SelectStatement tempSelect = tt.buildSelect(sc);
// Build and plan the temp table update statement.
final UpdateStatement tempTableUpdate = new UpdateStatement();
tempTableUpdate.setIgnore(us.getIgnore());
tempTableUpdate.setAliases(new AliasInformation());
tempTableUpdate.setTables(tempSelect.getTables());
final TempTableInstance tti = new TempTableInstance(sc, tt);
/*
* Replace the column names from the original update expressions
* with their temp table counterparts.
*/
final List<ExpressionNode> tempTableUpdateExpressions = new ArrayList<ExpressionNode>();
for (final Pair<Integer,RHSUpdateState> ind : updateOffsets) {
// the mapper is undoubtedly the worst thing I ever did. seriously.
ColumnInstance lhs = (ColumnInstance) ExpressionUtils.getTarget(tempSelect.getProjectionEdge().get(ind.getFirst()));
ExpressionNode rhs = ind.getSecond().getExpression(tempSelect);
tempTableUpdateExpressions.add(new FunctionCall(FunctionName.makeEquals(),(ExpressionNode)lhs.copy(null),rhs));
}
tempTableUpdate.setUpdateExpressions(tempTableUpdateExpressions);
tempTableUpdate.setWhereClause(tempSelect.getMapper().copyForward(updateStmtCopy.getWhereClause()));
tempTableUpdate.getDerivedInfo().addLocalTable(tti.getTableKey());
FeatureStep tempTableUpdateStep =
buildPlan(tempTableUpdate,pc,DefaultFeaturePlannerFilter.INSTANCE);
// Finally, we redist back to the target table - but at this point we strip off all the extra columns we added
ProjectingFeatureStep selectFromTempStep =
tempTableStep.buildNewProjectingStep(pc, this, null, null);
tempSelect = (SelectStatement) selectFromTempStep.getPlannedStatement();
final List<PEColumn> targCols = updateTable.getColumns(sc);
final List<ExpressionNode> newProj = new ArrayList<ExpressionNode>();
List<PEColumn> cols = updateTable.getColumns(sc);
for(int i = 0; i < cols.size(); i++) {
ExpressionNode en = tempSelect.getProjectionEdge().get(i);
if (en instanceof ExpressionAlias) {
final ExpressionAlias ea = (ExpressionAlias) en;
ea.setAlias(targCols.get(i).getName().getUnqualified());
newProj.add(ea);
} else {
final ExpressionAlias ea = new ExpressionAlias(en, new NameAlias(targCols.get(i).getName().getUnqualified()), false);
newProj.add(ea);
}
}
tempSelect.setProjection(newProj);
final RedistFeatureStep backToSource =
new RedistFeatureStep(this, selectFromTempStep, btk, updateTable.getStorageGroup(sc),
Collections.<Integer> emptyList(),
new RedistributionFlags().withInsertIgnore());
MultiFeatureStep out = new MultiFeatureStep(this) {
public void schedule(PlannerContext sc, ExecutionSequence es, Set<FeatureStep> scheduled) throws PEException {
schedulePrefix(sc,es,scheduled);
getSelfChildren().get(0).schedule(sc, es, scheduled);
LateBindingOperationFilter.schedule(sc, getSelfChildren().get(1), es, scheduled, new LateBindingUpdateCounter());
for(int i = 2; i < getSelfChildren().size(); i++)
getSelfChildren().get(i).schedule(sc,es,scheduled);
}
};
out.withDefangInvariants();
out.addChild(tempTableStep);
out.addChild(tempTableUpdateStep);
out.addChild(deleteStep);
out.addChild(backToSource);
return out;
}
private FeatureStep planSimpleUpdate(final PlannerContext pc, final UpdateStatement us) throws PEException {
final SchemaContext sc = pc.getContext();
UpdateStatement copy = CopyVisitor.copy(us);
final ListOfPairs<ColumnKey, ExpressionNode> updateExpressions = getUpdateExpressions(copy);
TableKey updatedTable = getUpdateTables(updateExpressions);
List<ExpressionNode> projection = new ArrayList<ExpressionNode>();
List<ColumnKey> pks = new ArrayList<ColumnKey>();
PEKey pk = updatedTable.getAbstractTable().asTable().getUniqueKey(sc);
for(PEColumn pec : pk.getColumns(sc)) {
ColumnKey ck = new ColumnKey(updatedTable,pec);
pks.add(ck);
projection.add(ck.toInstance());
}
ListOfPairs<ColumnKey,RHSUpdateState> updateOffsets = new ListOfPairs<ColumnKey,RHSUpdateState>();
for (final Pair<ColumnKey, ExpressionNode> uex : updateExpressions) {
if (EngineConstant.CONSTANT.has(uex.getSecond())) {
updateOffsets.add(uex.getFirst(), new RHSUpdateState((ConstantExpression)uex.getSecond()));
} else {
updateOffsets.add(uex.getFirst(), new RHSUpdateState(projection.size()));
projection.add(uex.getSecond());
}
}
SelectStatement select = new SelectStatement(new AliasInformation())
.setTables(copy.getTables())
.setProjection(projection)
.setWhereClause(copy.getWhereClause());
select.setOrderBy(copy.getOrderBys());
select.setLimit(copy.getLimit());
select.getDerivedInfo().take(copy.getDerivedInfo());
SchemaMapper mapper = new SchemaMapper(copy.getMapper().getOriginals(), select, copy.getMapper().getCopyContext());
select.setMapper(mapper);
select.setLocking();
ProjectingFeatureStep selectStep =
(ProjectingFeatureStep) buildPlan(select,pc,DefaultFeaturePlannerFilter.INSTANCE);
// redist bcast
TableKey updateTable = pks.get(0).getTableKey();
PEStorageGroup targetGroup = updateTable.getAbstractTable().getStorageGroup(sc);
RedistFeatureStep lookupTableStep =
selectStep.redist(pc, this,
new TempTableCreateOptions(Model.BROADCAST, targetGroup),
null,
null);
SelectStatement lookupTable = lookupTableStep.buildNewSelect(pc);
List<ExpressionNode> stripped= new ArrayList<ExpressionNode>();
for(ExpressionNode en : lookupTable.getProjectionEdge()) {
stripped.add(ExpressionUtils.getTarget(en));
}
FromTableReference ftr = new FromTableReference(updateTable.toInstance());
List<ExpressionNode> eqs = new ArrayList<ExpressionNode>();
for(int i = 0; i < pks.size(); i++) {
eqs.add(new FunctionCall(FunctionName.makeEquals(),pks.get(i).toInstance(),stripped.get(i)));
}
JoinedTable jt = new JoinedTable(lookupTable.getBaseTables().get(0),ExpressionUtils.safeBuildAnd(eqs),JoinSpecification.INNER_JOIN);
ftr.addJoinedTable(jt);
List<ExpressionNode> mappedUpdateExprs = new ArrayList<ExpressionNode>();
for(int i = 0; i < updateOffsets.size(); i++) {
RHSUpdateState rstate = updateOffsets.get(i).getSecond();
ExpressionNode rhs = null;
if (rstate.getOffset() > -1) {
rhs = stripped.get(rstate.getOffset());
} else {
rhs = (ExpressionNode) rstate.getConstant().copy(null);
}
mappedUpdateExprs.add(new FunctionCall(FunctionName.makeEquals(),
updateOffsets.get(i).getFirst().toInstance(),rhs));
}
UpdateStatement uex = new UpdateStatement();
uex.setTables(Collections.singletonList(ftr));
uex.setUpdateExpressions(mappedUpdateExprs);
uex.setAliases(new AliasInformation());
uex.getDerivedInfo().addLocalTable(updateTable,lookupTable.getBaseTables().get(0).getTableKey());
FeatureStep out =
buildPlan(uex,pc,DefaultFeaturePlannerFilter.INSTANCE);
out.addChild(lookupTableStep);
return out;
}
}