/* * Copyright 2014-2015 the original author or authors * * Licensed under the Apache License, Version 2.0 (the “License”); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an “AS IS” BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.wplatform.ddal.command.dml; import java.util.ArrayList; import java.util.HashMap; import com.wplatform.ddal.command.CommandInterface; import com.wplatform.ddal.command.Prepared; import com.wplatform.ddal.command.expression.Expression; import com.wplatform.ddal.command.expression.Parameter; import com.wplatform.ddal.command.expression.ValueExpression; import com.wplatform.ddal.dbobject.Right; import com.wplatform.ddal.dbobject.table.Column; import com.wplatform.ddal.dbobject.table.PlanItem; import com.wplatform.ddal.dbobject.table.Table; import com.wplatform.ddal.dbobject.table.TableFilter; import com.wplatform.ddal.engine.Session; import com.wplatform.ddal.message.DbException; import com.wplatform.ddal.message.ErrorCode; import com.wplatform.ddal.result.ResultInterface; import com.wplatform.ddal.result.Row; import com.wplatform.ddal.result.RowList; import com.wplatform.ddal.util.New; import com.wplatform.ddal.util.StatementBuilder; import com.wplatform.ddal.util.StringUtils; import com.wplatform.ddal.value.Value; import com.wplatform.ddal.value.ValueNull; /** * This class represents the statement * UPDATE */ public class Update extends Prepared { private final ArrayList<Column> columns = New.arrayList(); private final HashMap<Column, Expression> expressionMap = New.hashMap(); private Expression condition; private TableFilter tableFilter; /** * The limit expression as specified in the LIMIT clause. */ private Expression limitExpr; public Update(Session session) { super(session); } public void setTableFilter(TableFilter tableFilter) { this.tableFilter = tableFilter; } public void setCondition(Expression condition) { this.condition = condition; } /** * Add an assignment of the form column = expression. * * @param column the column * @param expression the expression */ public void setAssignment(Column column, Expression expression) { if (expressionMap.containsKey(column)) { throw DbException.get(ErrorCode.DUPLICATE_COLUMN_NAME_1, column .getName()); } columns.add(column); expressionMap.put(column, expression); if (expression instanceof Parameter) { Parameter p = (Parameter) expression; p.setColumn(column); } } @Override public int update() { return updateRows(); } protected int updateRows() { tableFilter.startQuery(session); tableFilter.reset(); RowList rows = new RowList(session); try { Table table = tableFilter.getTable(); session.getUser().checkRight(table, Right.UPDATE); //table.lock(session, true, false); int columnCount = table.getColumns().length; // get the old rows, compute the new rows setCurrentRowNumber(0); int count = 0; Column[] columns = table.getColumns(); int limitRows = -1; if (limitExpr != null) { Value v = limitExpr.getValue(session); if (v != ValueNull.INSTANCE) { limitRows = v.getInt(); } } while (tableFilter.next()) { setCurrentRowNumber(count + 1); if (limitRows >= 0 && count >= limitRows) { break; } if (condition == null || Boolean.TRUE.equals(condition.getBooleanValue(session))) { Row oldRow = tableFilter.get(); Row newRow = table.getTemplateRow(); for (int i = 0; i < columnCount; i++) { Expression newExpr = expressionMap.get(columns[i]); Value newValue; if (newExpr == null) { newValue = oldRow.getValue(i); } else if (newExpr == ValueExpression.getDefault()) { Column column = table.getColumn(i); newValue = table.getDefaultValue(session, column); } else { Column column = table.getColumn(i); newValue = column.convert(newExpr.getValue(session)); } newRow.setValue(i, newValue); } table.validateConvertUpdateSequence(session, newRow); rows.add(oldRow); rows.add(newRow); count++; } } // TODO self referencing referential integrity constraints // don't work if update is multi-row and 'inversed' the condition! // probably need multi-row triggers with 'deleted' and 'inserted' // at the same time. anyway good for sql compatibility // TODO update in-place (but if the key changes, // we need to update all indexes) before row triggers // the cached row is already updated - we need the old values //table.updateRows(this, session, rows); return count; } finally { rows.close(); } } @Override public String getPlanSQL() { StatementBuilder buff = new StatementBuilder("UPDATE "); buff.append(tableFilter.getPlanSQL(false)).append("\nSET\n "); for (int i = 0, size = columns.size(); i < size; i++) { Column c = columns.get(i); Expression e = expressionMap.get(c); buff.appendExceptFirst(",\n "); buff.append(c.getName()).append(" = ").append(e.getSQL()); } if (condition != null) { buff.append("\nWHERE ").append(StringUtils.unEnclose(condition.getSQL())); } return buff.toString(); } @Override public void prepare() { if (condition != null) { condition.mapColumns(tableFilter, 0); condition = condition.optimize(session); condition.createIndexConditions(session, tableFilter); } for (int i = 0, size = columns.size(); i < size; i++) { Column c = columns.get(i); Expression e = expressionMap.get(c); e.mapColumns(tableFilter, 0); expressionMap.put(c, e.optimize(session)); } PlanItem item = tableFilter.getBestPlanItem(session, 1); tableFilter.setPlanItem(item); tableFilter.prepare(); } @Override public boolean isTransactional() { return true; } @Override public ResultInterface queryMeta() { return null; } @Override public int getType() { return CommandInterface.UPDATE; } public void setLimit(Expression limit) { this.limitExpr = limit; } @Override public boolean isCacheable() { return true; } //getter public ArrayList<Column> getColumns() { return columns; } public HashMap<Column, Expression> getExpressionMap() { return expressionMap; } public Expression getCondition() { return condition; } public TableFilter getTableFilter() { return tableFilter; } public Expression getLimitExpr() { return limitExpr; } }