package com.tesora.dve.sql.template;
/*
* #%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.List;
import org.apache.commons.lang.StringUtils;
import com.tesora.dve.common.catalog.FKMode;
import com.tesora.dve.common.catalog.TemplateMode;
import com.tesora.dve.exceptions.PEException;
import com.tesora.dve.sql.ParserException.Pass;
import com.tesora.dve.sql.SchemaException;
import com.tesora.dve.sql.parser.InvokeParser;
import com.tesora.dve.sql.parser.ParserOptions;
import com.tesora.dve.sql.schema.ContainerDistributionVector;
import com.tesora.dve.sql.schema.DistributionVector;
import com.tesora.dve.sql.schema.Name;
import com.tesora.dve.sql.schema.PEColumn;
import com.tesora.dve.sql.schema.PEContainer;
import com.tesora.dve.sql.schema.PEDatabase;
import com.tesora.dve.sql.schema.PETable;
import com.tesora.dve.sql.schema.PETemplate;
import com.tesora.dve.sql.schema.Persistable;
import com.tesora.dve.sql.schema.RangeDistribution;
import com.tesora.dve.sql.schema.RangeDistributionVector;
import com.tesora.dve.sql.schema.SchemaContext;
import com.tesora.dve.sql.schema.UnqualifiedName;
import com.tesora.dve.sql.schema.VectorRange;
import com.tesora.dve.sql.schema.cache.CacheType;
import com.tesora.dve.sql.schema.cache.SchemaCacheKey;
import com.tesora.dve.sql.statement.Statement;
import com.tesora.dve.sql.statement.ddl.PECreateStatement;
import com.tesora.dve.sql.template.jaxb.RequirementType;
import com.tesora.dve.sql.template.jaxb.TableTemplateType;
import com.tesora.dve.sql.util.Pair;
public final class TemplateManager {
private TemplateManager() {
}
public static Pair<Name, TemplateMode> findTemplateForDatabase(final SchemaContext sc, final Name dbName, final Name templateName,
final TemplateMode templateMode) {
if (dbName == null) {
throw new NullPointerException("database name");
}
if (templateName != null) {
// if specified, ensure the template exists
if (hasTemplate(sc, templateName)) {
return new Pair<Name, TemplateMode>(templateName, templateMode);
}
throw new SchemaException(Pass.SECOND, "No such template: '" + templateName.getSQL() + "'");
} else {
// not specified, look for a match
final Name matchedTemplate = findTemplateMatchForDatabase(sc, dbName);
if ((matchedTemplate != null) || !templateMode.requiresTemplate()) {
return new Pair<Name, TemplateMode>(matchedTemplate, templateMode);
}
throw new SchemaException(Pass.SECOND, "Template required, but not matched");
}
}
public static Name findTemplateMatchForDatabase(final SchemaContext sc, final Name dbName) {
final List<PETemplate> matchingTemplates = sc.findMatchTemplates();
for (PETemplate pet : matchingTemplates) {
if (pet.isMatch(dbName.getUnqualified())) {
return pet.getName().getUnquotedName();
}
}
return null;
}
public static boolean hasTemplate(final SchemaContext sc, final Name templateName) {
return (sc.findTemplate(templateName) != null);
}
public static PETemplate findTemplate(SchemaContext sc, String templateName) {
if (templateName != null) {
return sc.findTemplate(new UnqualifiedName(templateName));
}
return null;
}
public static boolean inject(SchemaContext sc, PEDatabase peds, PETable tab) {
if (tab.getDistributionVector(sc) != null)
return false;
PETemplate pet = findTemplate(sc,peds.getTemplateName());
if (pet == null)
return false;
TableTemplateType ttt = pet.findMatch(tab.getName().get());
if (ttt == null)
return false;
ArrayList<PEColumn> columns = new ArrayList<PEColumn>();
for(String cname : ttt.getColumn()) {
PEColumn c = tab.lookup(sc, cname);
if (c == null)
throw new SchemaException(Pass.SECOND,"Missing column for inject distribution vector on table " + tab.getName().getSQL() + ": " + cname);
columns.add(c);
}
DistributionVector.Model model = DistributionVector.Model.getModelFromPersistent(ttt.getModel().value());
if (model != null) {
DistributionVector dv = null;
if (model == DistributionVector.Model.RANGE) {
String rangeName = ttt.getRange();
if (rangeName == null)
throw new SchemaException(Pass.SECOND, "Malformed table template - range model specified but no range specified");
RangeDistribution rd = sc.findRange(new UnqualifiedName(rangeName), tab.getPersistentStorage(sc).getName());
if (rd == null)
throw new SchemaException(Pass.SECOND,"No such range from template '" + peds.getTemplateName() + "' on storage group " + tab.getPersistentStorage(sc).getName() + ": " + rangeName);
dv = new RangeDistributionVector(sc, columns, false, new VectorRange(sc,rd));
} else if (model == DistributionVector.Model.CONTAINER) {
String containerName = ttt.getContainer();
if (!StringUtils.isBlank(containerName)) {
PEContainer container = sc.findContainer(new UnqualifiedName(containerName));
if (container == null) {
throw new SchemaException(Pass.SECOND, "No such container from template '" + peds.getTemplateName() + "': " + containerName);
}
dv = new ContainerDistributionVector(sc,container,false);
if (ttt.getDiscriminator().size() > 0) {
for(int i = 0; i < ttt.getDiscriminator().size(); i++) {
UnqualifiedName un = new UnqualifiedName(ttt.getDiscriminator().get(i));
PEColumn pec = tab.lookup(sc, un);
if (pec == null)
throw new SchemaException(Pass.SECOND, "No such column: " + un.getSQL() + " - cannot build discriminator");
pec.setContainerDistributionValuePosition(i + 1);
}
container.setBaseTable(sc, tab);
}
}
} else {
dv = new DistributionVector(sc,columns,model);
}
tab.setDistributionVector(sc,dv);
}
return true;
}
public static List<Statement> adaptPrereqs(SchemaContext pc, PEDatabase peds) throws PEException {
if (peds.getTemplateName() == null)
return Collections.emptyList();
PETemplate pet;
try {
pet = findTemplate(pc, peds.getTemplateName());
} catch (Exception e) {
throw new PEException("Error finding template", e);
}
if (pet == null)
return Collections.emptyList();
if (pet.getTemplate().getFkmode() != null) {
FKMode obj = FKMode.toMode(pet.getTemplate().getFkmode().value());
if (peds.getFKMode() == null)
peds.setFKMode(obj);
}
ArrayList<Statement> ret = new ArrayList<Statement>();
// we can reuse the persistence context, but make sure we change the options
SchemaContext usepc = pc;
ParserOptions prevOptions = usepc.getOptions();
ParserOptions subOptions = prevOptions.setAllowDuplicates();
try {
for(RequirementType rt : pet.getTemplate().getRequirement()) {
String raw = rt.getDeclaration();
String sqlcommand = raw.replaceAll("#sg#", peds.getDefaultStorage(pc).getName().get());
List<Statement> prereq = InvokeParser.parse(InvokeParser.buildInputState(sqlcommand,usepc), subOptions, usepc).getStatements();
for(Statement s : prereq) {
if (s instanceof PECreateStatement) {
PECreateStatement<?,?> pecs = (PECreateStatement<?, ?>) s;
if (pecs.isNew()) {
ret.add(s);
if (usepc.getSource().getType() == CacheType.MUTABLE) {
Persistable<?,?> targ = pecs.getRoot();
SchemaCacheKey<?> sck = targ.getCacheKey();
if (sck != null)
usepc.getSource().setLoaded(targ, sck);
}
}
} else {
ret.add(s);
}
}
}
} finally {
usepc.setOptions(prevOptions);
}
return ret;
}
}