package com.tesora.dve.sql.statement.ddl.alter; /* * #%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.List; import org.apache.commons.lang.StringUtils; import com.tesora.dve.sql.schema.Name; import com.tesora.dve.sql.schema.PEColumn; import com.tesora.dve.sql.schema.PETable; import com.tesora.dve.sql.schema.SchemaContext; import com.tesora.dve.sql.transform.execution.CatalogModificationExecutionStep.Action; import com.tesora.dve.sql.util.Functional; import com.tesora.dve.sql.util.Pair; import com.tesora.dve.sql.util.UnaryFunction; public class AddColumnAction extends AbstractAlterColumnAction { private List<PEColumn> newColumns; private Pair<String, Name> firstOrAfterSpec; public AddColumnAction(List<PEColumn> columns) { this(columns, null); } public AddColumnAction(List<PEColumn> columns, Pair<String, Name> firstOrAfterSpec) { newColumns = columns; for(PEColumn p : newColumns) p.normalize(); this.firstOrAfterSpec = firstOrAfterSpec; } public List<PEColumn> getNewColumns() { return newColumns; } @Override public AlterTableAction alterTable(SchemaContext sc, PETable tab) { List<PEColumn> movedCols = null; if (firstOrAfterSpec != null) { // take a first pass and update the ordinal numbers int ordinal = -1; if (StringUtils.equalsIgnoreCase(firstOrAfterSpec.getFirst(), "AFTER")) { PEColumn afterColumn = tab.lookup(sc, firstOrAfterSpec.getSecond().getUnqualified().getUnquotedName().get()); ordinal = afterColumn.getPosition(); } movedCols = new ArrayList<PEColumn>(); List<PEColumn> origCols = tab.getColumns(sc); for(PEColumn col : origCols) { if (col.getPosition() > ordinal) { movedCols.add(col); } } for(PEColumn col : movedCols) { tab.removeColumn(sc, col); } } for(PEColumn nc : newColumns) { if (nc.getIn(sc, tab) == null) { PEColumn copy = (PEColumn) nc.copy(sc, null); tab.addColumn(sc, copy); // add back the columns if FIRST or AFTER was specified if (movedCols != null) { for(PEColumn c : movedCols) { tab.addColumn(sc, c); } } } } return null; } @Override public boolean isNoop(SchemaContext sc, PETable tab) { // only a noop if all are a noop for(PEColumn nc : newColumns) { if (nc.getIn(sc, tab) == null) { return false; } } return true; } @Override public String isValid(SchemaContext sc, PETable tab) { if (firstOrAfterSpec != null && firstOrAfterSpec.getSecond() != null) { // validate the after column exists if (tab.lookup(sc, firstOrAfterSpec.getSecond()) == null) { return "Table " + tab.getName() + " does not contain column " + firstOrAfterSpec.getSecond().getSQL() + " that was specified with AFTER. Cannot add."; } } // only valid if any of the columns doesn't already exist for(PEColumn nc : newColumns) { if (nc.getIn(sc, tab) == null) return null; } return "Table " + tab.getName() + " already contains column" + (newColumns.size() > 1 ? "s " : " ") + Functional.join(newColumns, ", ", new UnaryFunction<String,PEColumn>() { @Override public String evaluate(PEColumn object) { return object.getName().get(); } }) + ", cannot add"; } @Override public AlterTableAction adapt(SchemaContext sc, PETable actual) { return new AddColumnAction(newColumns); } @Override public Action getActionKind() { return Action.CREATE; } @Override public List<PEColumn> getColumns() { return newColumns; } public Pair<String, Name> getFirstOrAfterSpec() { return firstOrAfterSpec; } }