package com.tesora.dve.sql.schema;
/*
* #%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.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.tesora.dve.common.PEStringUtils;
import com.tesora.dve.common.catalog.DistributionModel;
import com.tesora.dve.common.catalog.PersistentGroup;
import com.tesora.dve.distribution.IColumnDatum;
import com.tesora.dve.distribution.IKeyValue;
import com.tesora.dve.distribution.KeyValue;
import com.tesora.dve.distribution.PELockedException;
import com.tesora.dve.distribution.RangeLimit;
import com.tesora.dve.exceptions.PECodingException;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.exceptions.PERuntimeException;
import com.tesora.dve.sql.expression.ColumnKey;
import com.tesora.dve.sql.expression.ExpressionPath;
import com.tesora.dve.sql.expression.TableKey;
import com.tesora.dve.sql.node.expression.ColumnInstance;
import com.tesora.dve.sql.node.expression.ConstantExpression;
import com.tesora.dve.sql.schema.DistributionVector.Model;
import com.tesora.dve.sql.schema.cache.ConstantType;
import com.tesora.dve.sql.transform.MatchableKey;
import com.tesora.dve.sql.transform.constraints.KeyConstraint;
import com.tesora.dve.sql.transform.constraints.PlanningConstraint;
import com.tesora.dve.sql.transform.constraints.PlanningConstraintType;
import com.tesora.dve.sql.util.Functional;
import com.tesora.dve.sql.util.ListOfPairs;
import com.tesora.dve.sql.util.Pair;
import com.tesora.dve.sql.util.UnaryFunction;
public class DistributionKey implements PlanningConstraint {
private final TableKey onTable;
private final Map<PEColumn, ConstantExpression> values;
private final List<PEColumn> columnOrder;
// for random inserts we choose the persistent site ahead of time
// so we can parallelize multituple inserts
private PEStorageSite siteOverride;
public DistributionKey(TableKey tab, List<PEColumn> order, ListOfPairs<PEColumn,ConstantExpression> filters) {
onTable = tab;
values = new HashMap<PEColumn, ConstantExpression>();
if (filters != null) {
for(Pair<PEColumn,ConstantExpression> p : filters)
values.put(p.getFirst(),p.getSecond());
}
columnOrder = order;
siteOverride = null;
}
public Model getModel(SchemaContext sc) {
return onTable.getAbstractTable().getDistributionVector(sc).getModel();
}
@Override
public List<PEColumn> getColumns(SchemaContext sc) {
return onTable.getAbstractTable().getDistributionVector(sc).getColumns(sc);
}
public void put(SchemaContext sc, ColumnKey ck, ConstantExpression litex) {
values.put(ck.getPEColumn(), litex);
}
public void put(SchemaContext sc, ColumnInstance c, ConstantExpression litex) {
values.put(c.getPEColumn(), litex);
}
public void setGroupOverride(PEStorageSite pegged) {
siteOverride = pegged;
}
@Override
public PlanningConstraintType getType() {
return PlanningConstraintType.DISTVECT;
}
@Override
public MatchableKey getKey(SchemaContext sc) {
return onTable.getAbstractTable().getDistributionVector(sc);
}
@Override
public Map<PEColumn, ConstantExpression> getValues() {
return values;
}
@Override
public PEAbstractTable<?> getTable() {
return onTable.getAbstractTable();
}
@Override
public TableKey getTableKey() {
return onTable;
}
@Override
public ExpressionPath getPath() {
return null;
}
@Override
public int compareTo(PlanningConstraint o) {
return KeyConstraint.compareTo(this, o);
}
@Override
public boolean sameUnderlyingKey(PlanningConstraint other) {
if (other instanceof DistributionKey) {
DistributionKey odk = (DistributionKey) other;
return (odk.getTable().equals(getTable()));
}
return false;
}
public String describe(final ConnectionValues cv) {
return "{" + Functional.join(columnOrder, ", ", new UnaryFunction<String, PEColumn>() {
@Override
public String evaluate(PEColumn object) {
ConstantExpression ce = values.get(object);
return object.getName().get() + "='" + (ce == null ? "unset" : ce.getValue(cv)) + "'";
}
}) + "}";
}
@Override
public long getRowCount() {
return -1;
}
public IKeyValue getDetachedKey(SchemaContext sc, ConnectionValues cv) {
// actualize the dist key so that it can be used without a context
// we use late binding if there are runtime constants or we are under trigger planning
// otherwise we'll use the actual value.
LinkedHashMap<PEColumn, TColumnDatumBase> vals = new LinkedHashMap<PEColumn, TColumnDatumBase>();
boolean late = false;
for(Map.Entry<PEColumn, ConstantExpression> me : values.entrySet()) {
TColumnDatumBase value = null;
if (me.getValue().getConstantType() == ConstantType.RUNTIME
|| (sc.getOptions() != null && sc.getOptions().isNestedPlan())) {
late = true;
value = new DeferredTColumnDatum(me.getKey(),me.getValue());
} else {
value = new TColumnDatum(me.getKey(),
me.getValue().convert(cv, me.getKey().getType()));
}
vals.put(me.getKey(), value);
}
return new DetachedKeyValue(sc,onTable.getAbstractTable(),vals,late,columnOrder,siteOverride);
}
public void setFrozen() {
onTable.setFrozen();
for(ConstantExpression ce : values.values()) {
ce.setParent(null);
}
}
private static class DetachedKeyValue implements IKeyValue {
private final PEAbstractTable<?> table;
private final LinkedHashMap<PEColumn, TColumnDatumBase> values; //NOPMD
private final SchemaContext context;
private LinkedHashMap<String, TColumnDatum> externalValues = null; //NOPMD
private final List<PEColumn> columnOrder;
private final PEStorageSite pegged;
private final boolean hasDeferredValues;
public DetachedKeyValue(SchemaContext sc, PEAbstractTable<?> ontab, LinkedHashMap<PEColumn,TColumnDatumBase> vals, boolean lateVals,List<PEColumn> cols, PEStorageSite peggedGroup) { //NOPMD
table = ontab;
values = vals;
context = sc;
columnOrder = cols;
pegged = peggedGroup;
this.hasDeferredValues = lateVals;
}
@Override
public int getUserTableId() {
return table.getPersistentID();
}
@Override
public String getQualifiedTableName() {
return table.getQualifiedPersistentName(context);
}
@Override
public int compare(IKeyValue other) throws PEException {
if (hasDeferredValues)
throw new PECodingException("Attempt to compare a deferred key");
return KeyValue.compare(this,other);
}
@Override
public int compare(RangeLimit rangeLimit) throws PEException {
if (hasDeferredValues)
throw new PECodingException("Attempt to compare a deferred key");
return KeyValue.compare(this, rangeLimit);
}
@Override
public PersistentGroup getPersistentGroup() {
if (pegged != null) try {
return new PersistentGroup(pegged.getPersistent(context));
} catch (PELockedException pele) {
}
return table.getPersistent(context).getPersistentGroup();
}
@Override
public int getPersistentGroupId() {
if (pegged != null)
// special value - means we have to look up the persistent group anyways
return -1;
return table.getPersistentStorage(context).getPersistentID();
}
@Override
public DistributionModel getDistributionModel() {
return table.getDistributionVector(context).getModel().getSingleton();
}
@Override
public LinkedHashMap<String, ? extends IColumnDatum> getValues() { //NOPMD
if (hasDeferredValues)
throw new PERuntimeException("Attempt to getValues from deferred key");
if (externalValues == null) {
externalValues = new LinkedHashMap<String,TColumnDatum>();
for(PEColumn c : columnOrder) {
TColumnDatum tcd = (TColumnDatum) values.get(c);
if (tcd != null) externalValues.put(
tcd.getColumn().getPersistentName(),tcd);
}
}
return externalValues;
}
@Override
public int hashCode() {
if (hasDeferredValues)
throw new PERuntimeException("Attempt to generate hash code for deferred key");
return KeyValue.buildHashCode(this);
}
@Override
public boolean equals(Object o) {
if (hasDeferredValues)
throw new PERuntimeException("Attempt to test equality on a deferred key");
return KeyValue.equals((IKeyValue)this,(IKeyValue)o);
}
@Override
public DistributionModel getContainerDistributionModel() {
if (table.getDistributionVector(context).getModel() != Model.CONTAINER)
return null;
return table.getDistributionVector(context).getContainer(context).getContainerDistributionModel().getSingleton();
}
@Override
public String toString() {
return PEStringUtils.toString(table.getName(context,context.getValues()).getUnquotedName().get(), getValues());
}
@Override
public Integer getRangeId() {
return table.getDistributionVector(context).getRangeID(context);
}
@Override
public IKeyValue rebind(ConnectionValues cv)
throws PEException {
if (!hasDeferredValues)
return this;
LinkedHashMap<PEColumn,TColumnDatumBase> boundVals = new LinkedHashMap<PEColumn,TColumnDatumBase>();
for(Map.Entry<PEColumn,TColumnDatumBase> me : values.entrySet())
boundVals.put(me.getKey(),me.getValue().bind(cv));
return new DetachedKeyValue(context,table,boundVals,false,columnOrder,null);
}
}
}