package com.tesora.dve.sql.transform.strategy.triggers;
/*
* #%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.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.queryplan.TriggerValueHandler;
import com.tesora.dve.queryplan.TriggerValueHandlers;
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.TableKey;
import com.tesora.dve.sql.node.expression.ColumnInstance;
import com.tesora.dve.sql.node.expression.ExpressionNode;
import com.tesora.dve.sql.node.expression.TableInstance;
import com.tesora.dve.sql.node.structural.FromTableReference;
import com.tesora.dve.sql.node.structural.SortingSpecification;
import com.tesora.dve.sql.schema.PEColumn;
import com.tesora.dve.sql.schema.PEKey;
import com.tesora.dve.sql.schema.PETable;
import com.tesora.dve.sql.schema.PETableTriggerPlanningEventInfo;
import com.tesora.dve.sql.schema.TriggerEvent;
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.transform.CopyVisitor;
import com.tesora.dve.sql.transform.SchemaMapper;
import com.tesora.dve.sql.transform.strategy.FeaturePlannerIdentifier;
import com.tesora.dve.sql.transform.strategy.PlannerContext;
import com.tesora.dve.sql.transform.strategy.featureplan.FeatureStep;
import com.tesora.dve.sql.util.Functional;
import com.tesora.dve.sql.util.ListSet;
import com.tesora.dve.sql.util.Pair;
import com.tesora.dve.sql.util.UnaryFunction;
public class DeleteTriggerPlanner extends TriggerPlanner {
@Override
public FeatureStep plan(DMLStatement stmt, PlannerContext context)
throws PEException {
DeleteStatement ds = (DeleteStatement) stmt;
List<TableKey> deleteTabs = Functional.apply(ds.getTargetDeletes(), new UnaryFunction<TableKey,TableInstance>() {
@Override
public TableKey evaluate(TableInstance object) {
return object.getTableKey();
}
});
ListSet<PETable> triggered = getTriggeredTables(context,deleteTabs,TriggerEvent.DELETE);
if (triggered.isEmpty()) return null;
if (triggered.size() > 1)
throw new SchemaException(Pass.PLANNER,"Too many triggered tables");
PETable subject = triggered.get(0);
TableKey deleteKey = deleteTabs.get(0);
PETableTriggerPlanningEventInfo triggerInfo = (PETableTriggerPlanningEventInfo) subject.getTriggers(context.getContext(), TriggerEvent.DELETE);
LinkedHashMap<PEColumn,Integer> uniqueKeyOffsets = new LinkedHashMap<PEColumn,Integer>();
Pair<TriggerValueHandlers,SelectStatement> srcSelect = buildTempTableSelect(context,ds,deleteKey,triggerInfo,uniqueKeyOffsets);
DeleteStatement targetDelete = buildUniqueKeyDelete(context,deleteKey,uniqueKeyOffsets);
return commonPlanning(context,deleteKey,srcSelect.getSecond(),srcSelect.getFirst(),targetDelete,triggerInfo);
}
@Override
public FeaturePlannerIdentifier getFeaturePlannerID() {
return FeaturePlannerIdentifier.DELETE_TRIGGER;
}
private Pair<TriggerValueHandlers,SelectStatement> buildTempTableSelect(PlannerContext context, DeleteStatement ds, TableKey triggerTable,
PETableTriggerPlanningEventInfo triggerInfo,
LinkedHashMap<PEColumn,Integer> uniqueKeyOffsets) throws PEException {
PEKey uk = triggerTable.getAbstractTable().asTable().getUniqueKey(context.getContext());
if (uk == null)
throw new PEException("No support for deleting a table with delete triggers but no unique key");
ListSet<PEColumn> ukColumns = new ListSet<PEColumn>(uk.getColumns(context.getContext()));
DeleteStatement copy = CopyVisitor.copy(ds);
SelectStatement out = new SelectStatement(new AliasInformation())
.setTables(copy.getTables())
.setWhereClause(copy.getWhereClause());
out.setOrderBy(copy.getOrderBys());
out.setLimit(copy.getLimit());
out.getDerivedInfo().take(copy.getDerivedInfo());
SchemaMapper mapper = new SchemaMapper(copy.getMapper().getOriginals(), out, copy.getMapper().getCopyContext());
out.setMapper(mapper);
List<ExpressionNode> proj = new ArrayList<ExpressionNode>();
Collection<ColumnKey> triggerColumns = triggerInfo.getTriggerBodyColumns(context.getContext());
List<TriggerValueHandler> types = new ArrayList<TriggerValueHandler>();
for(ColumnKey ck : triggerColumns) {
int position = proj.size();
proj.add(new ColumnInstance(ck.getPEColumn(),triggerTable.toInstance()));
types.add(new TriggerValueHandler(ck.getPEColumn().getType()));
if (ukColumns.contains(ck.getPEColumn())) {
Integer any = uniqueKeyOffsets.get(ck.getPEColumn());
if (any == null)
uniqueKeyOffsets.put(ck.getPEColumn(),position);
}
}
for(PEColumn pec : ukColumns) {
Integer any = uniqueKeyOffsets.get(pec);
if (any == null) {
uniqueKeyOffsets.put(pec, proj.size());
proj.add(new ColumnInstance(pec,triggerTable.toInstance()));
types.add(new TriggerValueHandler(pec.getType()));
}
}
out.setProjection(proj);
return new Pair<TriggerValueHandlers,SelectStatement>(new TriggerValueHandlers(types),out);
}
private DeleteStatement buildUniqueKeyDelete(PlannerContext context, TableKey deleteTable,
LinkedHashMap<PEColumn,Integer> uniqueKeyOffsets) {
FromTableReference ftr = new FromTableReference(deleteTable.toInstance());
DeleteStatement out = new DeleteStatement(Collections.<TableInstance> emptyList(),
Collections.singletonList(ftr),
buildUniqueWhereClause(deleteTable,uniqueKeyOffsets),
Collections.<SortingSpecification> emptyList(),
null,
false,
new AliasInformation(),
null);
return out;
}
}