package com.tesora.dve.sql.statement.dml; /* * #%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.Arrays; import java.util.List; import com.tesora.dve.sql.SchemaException; import com.tesora.dve.sql.ParserException.Pass; import com.tesora.dve.sql.node.Edge; import com.tesora.dve.sql.node.EdgeName; import com.tesora.dve.sql.node.LanguageNode; import com.tesora.dve.sql.node.SingleEdge; import com.tesora.dve.sql.node.expression.ColumnInstance; import com.tesora.dve.sql.node.expression.ExpressionNode; import com.tesora.dve.sql.node.expression.LiteralExpression; import com.tesora.dve.sql.node.expression.TableInstance; import com.tesora.dve.sql.parser.SourceLocation; import com.tesora.dve.sql.schema.Column; import com.tesora.dve.sql.schema.PEColumn; import com.tesora.dve.sql.schema.PETable; import com.tesora.dve.sql.schema.SQLMode; import com.tesora.dve.sql.schema.SchemaContext; import com.tesora.dve.sql.statement.StatementType; import com.tesora.dve.sql.transform.execution.ExecutionType; import com.tesora.dve.sql.util.Functional; import com.tesora.dve.sql.util.ListSet; import com.tesora.dve.sql.util.UnaryFunction; import com.tesora.dve.variables.KnownVariables; public class InsertIntoSelectStatement extends InsertStatement { private SingleEdge<InsertIntoSelectStatement, ProjectingStatement> sourceSelect = new SingleEdge<InsertIntoSelectStatement, ProjectingStatement>(InsertIntoSelectStatement.class, this, EdgeName.SUBQUERY); @SuppressWarnings("rawtypes") private List edges = Arrays.asList(new Edge[] { intoTable, columnSpec, sourceSelect, onDuplicateKey }); private boolean nestedGrouped; public InsertIntoSelectStatement( TableInstance tab, List<ExpressionNode> columns, ProjectingStatement ss, boolean nestedGrouped, List<ExpressionNode> onDupKey, AliasInformation aliasInfo, SourceLocation loc) { super(tab,columns,onDupKey,aliasInfo, loc); this.sourceSelect.set(ss); this.nestedGrouped = nestedGrouped; } public boolean isNestedGrouped() { return nestedGrouped; } public ProjectingStatement getSource() { return sourceSelect.get(); } public SingleEdge<InsertIntoSelectStatement, ProjectingStatement> getSourceEdge() { return sourceSelect; } @Override public void normalize(SchemaContext sc) { sourceSelect.get().normalize(sc); if (intoTable.get().getAbstractTable().isView()) throw new SchemaException(Pass.NORMALIZE, "No support for updatable views"); PETable tab = intoTable.get().getAbstractTable().asTable(); if (columnSpec.size() > tab.getColumns(sc).size()) throw new SchemaException(Pass.NORMALIZE, "More columns specified than exist in target table"); List<ExpressionNode> proj = sourceSelect.get().getProjections().get(0); if (proj.size() > tab.getColumns(sc).size()) throw new SchemaException(Pass.NORMALIZE, "More columns in projection than in table"); if (!columnSpec.isEmpty() && columnSpec.size() != proj.size()) { throw new SchemaException(Pass.NORMALIZE, "Column list not same size as projection"); } boolean isMT = sc.getPolicyContext().requiresMTRewrites(ExecutionType.INSERT); if (columnSpec.isEmpty()) { // nothing specified. if the number of values specified matches the number of columns (up to mt stuff), then assume // all the columns were specified. int visibleColumns = tab.getColumns(sc).size(); if (isMT) visibleColumns--; if (proj.size() == visibleColumns) { for(PEColumn c : tab.getColumns(sc)) { if (c.isTenantColumn()) continue; columnSpec.add(new ColumnInstance(null, c, intoTable.get())); } } } // missing columns // figure out what was specified first ListSet<Column<?>> specColumns = new ListSet<Column<?>>(); Functional.apply(columnSpec.getMulti(), specColumns, new UnaryFunction<Column<?>,ExpressionNode>() { @Override public Column<?> evaluate(ExpressionNode object) { ColumnInstance ci = (ColumnInstance) object; return ci.getColumn(); } }); SQLMode sqlMode = KnownVariables.SQL_MODE.getSessionValue(sc.getConnection().getVariableSource()); SelectStatement src = null; if (sourceSelect.get() instanceof SelectStatement) { src = (SelectStatement) sourceSelect.get(); } ListSet<PEColumn> autoincs = new ListSet<PEColumn>(); boolean added = false; for(PEColumn c : tab.getColumns(sc)) { if (specColumns.contains(c)) continue; if (c.isAutoIncrement()) { autoincs.add(c); continue; } columnSpec.add(new ColumnInstance(c,intoTable.get())); if (src == null) throw new SchemaException(Pass.NORMALIZE, "Unsupported: insert into select ... union ... where subquery normalization is required"); if (isMT && c.isTenantColumn()) { src.getProjectionEdge().add(sc.getPolicyContext().getTenantIDLiteral(true)); added = true; } else { ExpressionNode defaultValue = c.getDefaultValue(); if (defaultValue == null) { if (c.isNullable()) { src.getProjectionEdge().add(LiteralExpression.makeNullLiteral()); added = true; } else if (!sqlMode.isStrictMode()) { src.getProjectionEdge().add(c.getType().getZeroValueLiteral()); added = true; } else { // this is an error throw new SchemaException(Pass.NORMALIZE, "Missing projection value for non-nullable column " + c); } } else { src.getProjectionEdge().add((ExpressionNode) ((LiteralExpression)defaultValue).copy(null)); added = true; } } } // for autoincs, we place them at the end of the insert list, but not in the select for(PEColumn c : autoincs) { columnSpec.add(new ColumnInstance(c, intoTable.get())); } if (added) sourceSelect.get().normalize(sc); assertValidDupKey(sc); } @SuppressWarnings("unchecked") @Override public <T extends Edge<?,?>> List<T> getEdges() { return edges; } @Override public StatementType getStatementType() { return StatementType.INSERT_INTO_SELECT; } @Override protected boolean schemaSelfEqual(LanguageNode other) { InsertIntoSelectStatement oth = (InsertIntoSelectStatement) other; return nestedGrouped == oth.nestedGrouped; } @Override protected int selfHashCode() { return addSchemaHash(1,nestedGrouped); } }