/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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/>.
*/
package com.foundationdb.sql.optimizer.rule;
import com.foundationdb.server.error.UnsupportedCreateSelectException;
import com.foundationdb.sql.optimizer.plan.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
/** {@Create Table As Rules} takes in a planContext then visits all nodes that are
* instances of TableSource and replaces them with CreateAs plan, these are used
* later on to put EmitBoundRow_Nexted operators which will be used for insertion
* and deletion from an online Create Table As query*/
/**
* TODO in future versions this could take a specified table name or id
* then only change these tablesources, this would be necessary if in future versions
* we accept queries with union, intersect, except, join, etc
*/
public class CreateTableAsRules extends BaseRule {
private static final Logger logger = LoggerFactory.getLogger(SortSplitter.class);
@Override
protected Logger getLogger() {
return logger;
}
@Override
public void apply(PlanContext plan) {
Results results = new CreateTableAsFinder().find(plan.getPlan());
CreateAs createAs = null;
for (TableSource tableSource : results.tables) {
createAs = transform(tableSource);
}
assert(createAs != null);
for (Project project : results.projects) {
transform(project, createAs);
}
}
protected CreateAs transform(TableSource tableSource) {
CreateAs createAs = new CreateAs();
createAs.setOutput(tableSource.getOutput());
(tableSource.getOutput()).replaceInput(tableSource, createAs);
createAs.setTableSource(tableSource);
return createAs;
}
protected void transform(Project project, CreateAs createAs){
for (int i = 0; i < project.getFields().size(); i++){
if(project.getFields().get(i) instanceof ColumnExpression) {
ColumnExpression expression = (ColumnExpression) project.getFields().get(i);
project.getFields().remove(i);
project.getFields().add(i, new ColumnExpression(expression, createAs));
}
}
}
static class Results {
public List<TableSource> tables = new ArrayList<>();
public List<Project> projects = new ArrayList<>();
}
static class CreateTableAsFinder implements PlanVisitor {
Results results;
public Results find(PlanNode root) {
results = new Results();
root.accept(this);
return results;
}
@Override
public boolean visitEnter(PlanNode n) {
return visit(n);
}
@Override
public boolean visitLeave(PlanNode n) {
return true;
}
@Override
public boolean visit(PlanNode n) {
if(isIllegalPlan(n)){
throw new UnsupportedCreateSelectException();
}
if (n instanceof TableSource)
results.tables.add((TableSource) n);
else if (n instanceof Project) {
results.projects.add((Project) n);
}
return true;
}
public boolean isIllegalPlan(PlanNode n) {
// Only the simplest select from a single table is allowed.
return !(n instanceof DMLStatement || n instanceof InsertStatement ||
n instanceof Project || n instanceof Select || n instanceof TableSource);
}
}
}