001: /*
002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
003: * (http://h2database.com/html/license.html).
004: * Initial Developer: H2 Group
005: */
006: package org.h2.command.dml;
007:
008: import java.sql.SQLException;
009:
010: import org.h2.command.Prepared;
011: import org.h2.constant.ErrorCode;
012: import org.h2.engine.Right;
013: import org.h2.engine.Session;
014: import org.h2.expression.Expression;
015: import org.h2.expression.ValueExpression;
016: import org.h2.message.Message;
017: import org.h2.result.LocalResult;
018: import org.h2.result.Row;
019: import org.h2.result.RowList;
020: import org.h2.table.Column;
021: import org.h2.table.PlanItem;
022: import org.h2.table.Table;
023: import org.h2.table.TableFilter;
024: import org.h2.util.StringUtils;
025: import org.h2.value.Value;
026:
027: /**
028: * This class represents the statement
029: * UPDATE
030: */
031: public class Update extends Prepared {
032:
033: private Expression condition;
034: private TableFilter tableFilter;
035: private Expression[] expressions;
036:
037: public Update(Session session) {
038: super (session);
039: }
040:
041: public void setTableFilter(TableFilter tableFilter) {
042: this .tableFilter = tableFilter;
043: Table table = tableFilter.getTable();
044: expressions = new Expression[table.getColumns().length];
045: }
046:
047: public void setCondition(Expression condition) {
048: this .condition = condition;
049: }
050:
051: /**
052: * Add an assignment of the form column = expression.
053: *
054: * @param column the column
055: * @param expression the expression
056: */
057: public void setAssignment(Column column, Expression expression)
058: throws SQLException {
059: int id = column.getColumnId();
060: if (expressions[id] != null) {
061: throw Message
062: .getSQLException(ErrorCode.DUPLICATE_COLUMN_NAME_1,
063: column.getName());
064: }
065: expressions[id] = expression;
066: }
067:
068: public int update() throws SQLException {
069: tableFilter.startQuery(session);
070: tableFilter.reset();
071: RowList rows = new RowList(session);
072: try {
073: Table table = tableFilter.getTable();
074: session.getUser().checkRight(table, Right.UPDATE);
075: table.fireBefore(session);
076: table.lock(session, true, false);
077: int columnCount = table.getColumns().length;
078: // get the old rows, compute the new rows
079: setCurrentRowNumber(0);
080: int count = 0;
081: while (tableFilter.next()) {
082: checkCancelled();
083: setCurrentRowNumber(count + 1);
084: if (condition == null
085: || Boolean.TRUE.equals(condition
086: .getBooleanValue(session))) {
087: Row oldRow = tableFilter.get();
088: Row newRow = table.getTemplateRow();
089: for (int i = 0; i < columnCount; i++) {
090: Expression newExpr = expressions[i];
091: Value newValue;
092: if (newExpr == null) {
093: newValue = oldRow.getValue(i);
094: } else if (newExpr == ValueExpression.DEFAULT) {
095: Column column = table.getColumn(i);
096: Expression defaultExpr = column
097: .getDefaultExpression();
098: Value v;
099: if (defaultExpr == null) {
100: v = column
101: .validateConvertUpdateSequence(
102: session, null);
103: } else {
104: v = defaultExpr.getValue(session);
105: }
106: int type = column.getType();
107: newValue = v.convertTo(type);
108: } else {
109: Column column = table.getColumn(i);
110: newValue = newExpr.getValue(session)
111: .convertTo(column.getType());
112: }
113: newRow.setValue(i, newValue);
114: }
115: table
116: .validateConvertUpdateSequence(session,
117: newRow);
118: if (table.fireRow()) {
119: table.fireBeforeRow(session, oldRow, newRow);
120: }
121: rows.add(oldRow);
122: rows.add(newRow);
123: count++;
124: }
125: }
126: // TODO self referencing referential integrity constraints
127: // don't work if update is multi-row and 'inversed' the condition!
128: // probably need multi-row triggers with 'deleted' and 'inserted'
129: // at the same time. anyway good for sql compatibility
130: // TODO update in-place (but if the position changes,
131: // we need to update all indexes) before row triggers
132:
133: // the cached row is already updated - we need the old values
134: table.updateRows(this , session, rows);
135: if (table.fireRow()) {
136: rows.invalidateCache();
137: for (rows.reset(); rows.hasNext();) {
138: checkCancelled();
139: Row o = rows.next();
140: Row n = rows.next();
141: table.fireAfterRow(session, o, n);
142: }
143: }
144: table.fireAfter(session);
145: return count;
146: } finally {
147: rows.close();
148: }
149: }
150:
151: public String getPlanSQL() {
152: StringBuffer buff = new StringBuffer();
153: buff.append("UPDATE ");
154: buff.append(tableFilter.getPlanSQL(false));
155: buff.append("\nSET ");
156: Table table = tableFilter.getTable();
157: int columnCount = table.getColumns().length;
158: for (int i = 0, j = 0; i < columnCount; i++) {
159: Expression newExpr = expressions[i];
160: if (newExpr != null) {
161: if (j > 0) {
162: buff.append(",\n");
163: }
164: j++;
165: Column column = table.getColumn(i);
166: buff.append(column.getName());
167: buff.append(" = ");
168: buff.append(newExpr.getSQL());
169: }
170: }
171: if (condition != null) {
172: buff.append("\nWHERE "
173: + StringUtils.unEnclose(condition.getSQL()));
174: }
175: return buff.toString();
176: }
177:
178: public void prepare() throws SQLException {
179: if (condition != null) {
180: condition.mapColumns(tableFilter, 0);
181: condition = condition.optimize(session);
182: condition.createIndexConditions(session, tableFilter);
183: }
184: for (int i = 0; i < expressions.length; i++) {
185: Expression expr = expressions[i];
186: if (expr != null) {
187: expr.mapColumns(tableFilter, 0);
188: expressions[i] = expr.optimize(session);
189: }
190: }
191: PlanItem item = tableFilter.getBestPlanItem(session);
192: tableFilter.setPlanItem(item);
193: tableFilter.prepare();
194: }
195:
196: public boolean isTransactional() {
197: return true;
198: }
199:
200: public LocalResult queryMeta() {
201: return null;
202: }
203:
204: }
|