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.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.tesora.dve.common.MultiMap;
import com.tesora.dve.common.catalog.PersistentSite;
import com.tesora.dve.db.DBNative;
import com.tesora.dve.db.Emitter;
import com.tesora.dve.db.Emitter.EmitOptions;
import com.tesora.dve.db.Emitter.EmitterInvoker;
import com.tesora.dve.db.GenericSQLCommand;
import com.tesora.dve.exceptions.PEException;
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.ParserException.Pass;
import com.tesora.dve.sql.SchemaException;
import com.tesora.dve.sql.node.expression.ExpressionNode;
import com.tesora.dve.sql.statement.dml.InsertIntoValuesStatement;
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.worker.WorkerGroup.MappingSolution;
public class LateSortedInsert {
final InsertIntoValuesStatement stmt;
final ListOfPairs<List<ExpressionNode>,DistributionKey> parts;
public LateSortedInsert(final InsertIntoValuesStatement stmt, final ListOfPairs<List<ExpressionNode>, DistributionKey> parts) {
this.stmt = stmt;
this.parts = parts;
}
public List<JustInTimeInsert> resolve(SchemaContext sc, ConnectionValues cv) throws PEException {
// sort by persistent site
DistributionVector dv = this.stmt.getTable().getDistributionVector(sc);
LinkedHashMap<MappingSolution, DistributionKey> repKeys = new LinkedHashMap<MappingSolution, DistributionKey>();
MultiMap<MappingSolution, List<ExpressionNode>> bySite = new MultiMap<MappingSolution, List<ExpressionNode>>();
for (Pair<List<ExpressionNode>, DistributionKey> p : this.parts) {
DistributionKey dk = p.getSecond();
MappingSolution ms =
sc.getCatalog().mapKey(sc, dk.getDetachedKey(sc,cv), dk.getModel(sc), this.stmt.getKeyOpType(), this.stmt.getSingleGroup(sc), cv);
if (MappingSolution.AllWorkers == ms) {
throw new SchemaException(Pass.PLANNER, "Unable to sort inserts, key for model " + dk.getModel(sc) + " apparently not deterministic");
}
if (!repKeys.containsKey(ms))
repKeys.put(ms, dk);
bySite.put(ms, p.getFirst());
}
List<JustInTimeInsert> out = new ArrayList<JustInTimeInsert>();
// now, by site, build the actual insert
for(Map.Entry<MappingSolution, DistributionKey> me : repKeys.entrySet()) {
MappingSolution onSite = me.getKey();
DistributionKey dk = me.getValue();
if (dv.isRandom()) {
// override the group to be just the group we want
PersistentSite ss = (PersistentSite) onSite.getSite();
PEStorageSite pess = PEStorageSite.load(ss, sc);
dk.setGroupOverride(pess);
}
Collection<List<ExpressionNode>> values = bySite.get(onSite);
List<List<ExpressionNode>> asList = Functional.toList(values);
emitJITInsert(sc, cv, out, dk, asList);
}
return out;
}
private void emitJITInsert(final SchemaContext sc, final ConnectionValues cv, final List<JustInTimeInsert> out, final DistributionKey dk, final List<List<ExpressionNode>> asList) throws PEException {
Emitter emitter = Singletons.require(DBNative.class).getEmitter();
final GenericSQLCommand prefix = new EmitterInvoker(emitter) {
@Override
protected void emitStatement(final SchemaContext sc, final StringBuilder buf) {
getEmitter().emitInsertPrefix(sc, cv,LateSortedInsert.this.stmt, buf);
}
}.buildGenericCommand(sc);
final GenericSQLCommand suffix = new EmitterInvoker(emitter) {
@Override
protected void emitStatement(final SchemaContext sc, final StringBuilder buf) {
getEmitter().emitInsertSuffix(sc, cv,LateSortedInsert.this.stmt, buf);
}
}.buildGenericCommand(sc);
final EmitterInvoker valueEmitter = new EmitterInvoker(emitter) {
@Override
protected void emitStatement(final SchemaContext sc, final StringBuilder buf) {
getEmitter().emitInsertValues(sc, cv,asList, buf);
}
};
// we must always use generic sql - whether we have parameters or not,
// so that we can handle special characters correctly.
valueEmitter.getEmitter().setOptions(EmitOptions.GENERIC_SQL);
valueEmitter.getEmitter().startGenericCommand();
valueEmitter.getEmitter().pushContext(sc.getTokens());
try {
final GenericSQLCommand valuesClause = valueEmitter.buildGenericCommand(sc).resolve(cv, null);
final SQLCommand sqlc = reconstructSQLCommand(sc, prefix, valuesClause, suffix);
out.add(new JustInTimeInsert(sqlc, asList.size(), dk));
} finally {
valueEmitter.getEmitter().popContext();
}
}
private SQLCommand reconstructSQLCommand(final SchemaContext sc, final GenericSQLCommand prefix, final GenericSQLCommand valuesClause,
final GenericSQLCommand suffix) {
final GenericSQLCommand gsql = prefix.append(valuesClause).append(suffix);
if (sc.getValueManager().hasPassDownParams()) {
if ((sc.getOptions() != null && sc.getOptions().isPrepare())) {
return new SQLCommand(gsql);
}
return new SQLCommand(gsql, gsql.getFinalParams(sc));
}
return new SQLCommand(gsql);
}
}