001: /*
002: * $Id: UpdateCommand.java,v 1.64 2006/01/10 21:02:37 ahimanikya Exp $
003: * =======================================================================
004: * Copyright (c) 2002-2005 Axion Development Team. All rights reserved.
005: *
006: * Redistribution and use in source and binary forms, with or without
007: * modification, are permitted provided that the following conditions
008: * are met:
009: *
010: * 1. Redistributions of source code must retain the above
011: * copyright notice, this list of conditions and the following
012: * disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The names "Tigris", "Axion", nor the names of its contributors may
020: * not be used to endorse or promote products derived from this
021: * software without specific prior written permission.
022: *
023: * 4. Products derived from this software may not be called "Axion", nor
024: * may "Tigris" or "Axion" appear in their names without specific prior
025: * written permission.
026: *
027: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
028: * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
029: * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
030: * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
031: * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
032: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
033: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
034: * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
035: * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
036: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
037: * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
038: * =======================================================================
039: */
040:
041: package org.axiondb.engine.commands;
042:
043: import java.util.ArrayList;
044: import java.util.Iterator;
045: import java.util.List;
046:
047: import org.axiondb.AxionException;
048: import org.axiondb.ColumnIdentifier;
049: import org.axiondb.DataType;
050: import org.axiondb.Database;
051: import org.axiondb.Row;
052: import org.axiondb.RowDecorator;
053: import org.axiondb.RowIterator;
054: import org.axiondb.Selectable;
055: import org.axiondb.Table;
056: import org.axiondb.TableIdentifier;
057: import org.axiondb.engine.SnapshotIsolationTransaction;
058: import org.axiondb.engine.rowcollection.IntSet;
059: import org.axiondb.engine.rows.JoinedRow;
060: import org.axiondb.engine.rows.SimpleRow;
061: import org.axiondb.engine.tables.ExternalDatabaseTable;
062: import org.axiondb.engine.visitors.FindBindVariableVisitor;
063: import org.axiondb.engine.visitors.ResolveFromNodeVisitor;
064: import org.axiondb.jdbc.AxionResultSet;
065:
066: /**
067: * An <tt>UPDATE</tt> command.
068: *
069: * @version $Revision: 1.64 $ $Date: 2006/01/10 21:02:37 $
070: * @author Rodney Waldhoff
071: * @author Chuck Burdick
072: * @author Ahimanikya Satapathy
073: * @author Sudhendra Seshachala
074: * @author Ritesh Adval
075: */
076: public class UpdateCommand extends ChildTableUpdater {
077:
078: // ------------------------------------------------------------ Constructors
079:
080: public UpdateCommand() {
081: }
082:
083: // ---------------------------------------------------------- Public Methods
084:
085: public void addColumn(ColumnIdentifier col) {
086: _cols.add(col);
087: }
088:
089: public void addValue(Selectable val) {
090: _vals.add(val);
091: }
092:
093: public boolean execute(Database database) throws AxionException {
094: setEffectedRowCount(executeUpdate(database));
095: return false;
096: }
097:
098: /**
099: * Unsupported, use {@link #executeUpdate}instead.
100: *
101: * @throws UnsupportedOperationException
102: */
103: public AxionResultSet executeQuery(Database database)
104: throws AxionException {
105: throw new UnsupportedOperationException("Use executeUpdate.");
106: }
107:
108: public int executeUpdate(org.axiondb.Database db)
109: throws AxionException {
110: assertNotReadOnly(db);
111: preProcess(db);
112: resolve(db);
113:
114: int updatedRows = 0;
115: if (_context != null && _context.getFrom() != null) {
116: updatedRows = updateUsingFromClauseTables(db);
117: } else {
118: updatedRows = updateUsingStaticValues(db);
119: }
120:
121: return updatedRows;
122: }
123:
124: public int getColumnCount() {
125: return _cols.size();
126: }
127:
128: public Iterator getColumnIterator() {
129: return _cols.iterator();
130: }
131:
132: public ExceptionWhenClause getExceptionWhenClause() {
133: return _exceptionWhenClause;
134: }
135:
136: public TableIdentifier getTable() {
137: return _tableId;
138: }
139:
140: public int getValueCount() {
141: return _vals.size();
142: }
143:
144: public Iterator getValueIterator() {
145: return _vals.iterator();
146: }
147:
148: public Selectable getWhere() {
149: return _where;
150: }
151:
152: private void preProcess(Database db) throws AxionException {
153: _count = 0;
154:
155: _table = db.getTable(getTable());
156: if (null == _table) {
157: throw new AxionException("Table " + getTable()
158: + " not found.", 42704);
159: }
160: setDeferAllConstraintIfRequired(_table);
161:
162: if (_exceptionWhenClause != null) {
163: _exceptionWhenClause.preProcess(db);
164: }
165: }
166:
167: public void setExceptionWhenClause(DMLWhenClause w,
168: TableIdentifier t, List cols, List vals) {
169: _exceptionWhenClause = new ExceptionWhenClause(w, t, cols, vals);
170: }
171:
172: public void setQueryContext(AxionQueryContext context) {
173: _context = context;
174: _planner = new AxionQueryPlanner(context);
175: }
176:
177: public void setTable(TableIdentifier table) {
178: _tableId = table;
179: }
180:
181: public void setWhere(Selectable where) {
182: _where = where;
183: }
184:
185: protected void buildBindVariables() {
186: setBindVariableVisitor(new FindBindVariableVisitor());
187: getBindVariableVisitor().visit(this );
188: }
189:
190: private void commitIfRequired(Database db) throws AxionException {
191: if (getCommitSize(db) == 0) {
192: return;
193: }
194:
195: if (db instanceof SnapshotIsolationTransaction
196: && (++_count % getCommitSize(db)) == 0) {
197: _table = ((SnapshotIsolationTransaction) db)
198: .commit(_tableId);
199: }
200: }
201:
202: private Row prepareRow(Row oldrow, RowDecorator dec, Database db)
203: throws AxionException {
204: Table targetTable = _table;
205: Row newrow = new SimpleRow(oldrow);
206: Iterator colids = getColumnIterator();
207: Iterator values = this .getValueIterator();
208: while (colids.hasNext()) {
209: ColumnIdentifier colid = (ColumnIdentifier) (colids.next());
210: Selectable sel = (Selectable) (values.next());
211: Object val = sel.evaluate(dec);
212: DataType type = db.getTable(colid.getTableName())
213: .getColumn(colid.getName()).getDataType();
214: val = attemptToConvertValue(val, type, colid);
215: newrow
216: .set(targetTable.getColumnIndex(colid.getName()),
217: val);
218: }
219: return newrow;
220: }
221:
222: protected void resolve(Database db) throws AxionException {
223: if (!_resolved) {
224: resolveSelectableList(_cols, db, getTable());
225: resolveGeneratedColumns(_table, _tableId, _cols);
226:
227: // resolve FROM part
228: if (_context != null && _context.getFrom() != null) {
229: // resolve from node for any sub-select
230: ResolveFromNodeVisitor fnVisitor = new ResolveFromNodeVisitor();
231: fnVisitor.resolveFromNode(_context.getFrom(), db);
232: _context.setTables(_context.getFromArray());
233:
234: fnVisitor.resolveFromNode(_context.getFrom(), db, null);
235: resolveSelectableList(_vals, db, _context.getTables());
236: } else {
237: resolveSelectableList(_vals, db, getTable());
238: }
239:
240: // resolve WHERE part
241: if (getWhere() != null) {
242: setWhere(resolveSelectable(getWhere(), db,
243: new TableIdentifier[] { getTable() }));
244: }
245:
246: // check and resolve Exception When clause
247: if (_exceptionWhenClause != null) {
248: _exceptionWhenClause.resolve(db);
249: }
250: _isExternalDBTable = _table instanceof ExternalDatabaseTable;
251: _resolved = true;
252: }
253: }
254:
255: private int updateUsingFromClauseTables(Database db)
256: throws AxionException {
257: IntSet rowcount = new IntSet();
258: int extTblCnt = 0;
259: int colId = -1;
260: // 1. Execute - "SELECT * FROM targetTable LOJ srcTable/view"
261: List list = _context.getResolvedSelect();
262: _context.setSelected((Selectable[]) (list
263: .toArray(new Selectable[list.size()])));
264: RowIterator joinedRowIter = _planner.makeRowIterator(db, false);
265:
266: // 2. Build decorator now, build this before executing STEP 1
267: RowDecorator dec = new RowDecorator(_planner
268: .getColumnIdToFieldMap());
269:
270: // 3. Loop thru and merge(insert or update as appropriate)
271: while (joinedRowIter.hasNext()) {
272: // Since we createrd a LOJ assume the the joined row has
273: // left table's row should be at index(0)
274: // and right table's row at index(1)
275: JoinedRow joinRow = (JoinedRow) joinedRowIter.next();
276:
277: Row targetRow = joinRow.getRow(0); // get target table row
278: Row sourceRow = joinRow.getRow(1); // get source table row
279:
280: dec.setRow(joinedRowIter.currentIndex(), joinRow);
281:
282: // if current row match exception when condition process else
283: if (_exceptionWhenClause != null
284: && _exceptionWhenClause.insertMatchingRow(db, dec,
285: sourceRow)) {
286: continue; // pick next row
287: }
288:
289: // UPDATE: replace old row with new row
290: Row newrow = prepareRow(targetRow, dec, db);
291: if (_isExternalDBTable) {
292: ((ExternalDatabaseTable) _table).updateRow(targetRow,
293: newrow, _cols);
294: } else {
295: updateGeneratedValues(db, _table, _tableId, newrow);
296: if (!targetRow.equals(newrow)) {
297: _table.updateRow(targetRow, newrow);
298: updateOrSetNullChildRows(db, _table, targetRow,
299: newrow);
300: }
301: }
302: colId = newrow.getIdentifier();
303: if (colId == -1) {
304: extTblCnt++;
305: } else {
306: rowcount.add(colId);
307: }
308:
309: commitIfRequired(db);
310: }
311: return (rowcount.size() + extTblCnt);
312: }
313:
314: private int updateUsingStaticValues(Database db)
315: throws AxionException {
316: int rowcount = 0;
317: RowIterator iter = getRowIterator(db, getTable(), _table,
318: getWhere(), false, makeRowDecorator());
319: RowDecorator dec = makeRowDecorator();
320: while (iter.hasNext()) {
321: _count = 0;
322: Row oldrow = iter.next();
323: dec.setRow(iter.currentIndex(), oldrow);
324: Row newrow = prepareRow(oldrow, dec, db);
325:
326: // if current row match exception when condition process else
327: if (_exceptionWhenClause != null
328: && _exceptionWhenClause.insertMatchingRow(db, dec,
329: newrow)) {
330: continue;
331: }
332:
333: if (_isExternalDBTable) {
334: ((ExternalDatabaseTable) _table).updateRow(oldrow,
335: newrow, _cols);
336: } else {
337: updateGeneratedValues(db, _table, _tableId, newrow);
338: if (!oldrow.equals(newrow)) {
339: _table.updateRow(oldrow, newrow);
340: updateOrSetNullChildRows(db, _table, oldrow, newrow);
341: }
342: }
343: commitIfRequired(db);
344: rowcount++;
345: }
346:
347: return rowcount;
348: }
349:
350: private final RowDecorator makeRowDecorator() {
351: if (_dec == null) {
352: _dec = _table.makeRowDecorator();
353: }
354: return _dec;
355: }
356:
357: private class ExceptionWhenClause extends InsertIntoClause {
358: public ExceptionWhenClause(DMLWhenClause when,
359: TableIdentifier tid, List cols, List vals) {
360: super (when, tid, cols, vals);
361: }
362:
363: protected boolean isTargetTablePartOfSubQuery()
364: throws AxionException {
365: return _isTargetPartOfSubQuery;
366: }
367:
368: protected void resolve(Database db) throws AxionException {
369: super .resolve(db);
370:
371: if (_context != null) {
372: _isTargetPartOfSubQuery = _context
373: .isTablePartOfSelect(getTargetTableId());
374:
375: // resolve when condition
376: getWhenClause().resolve(db, _context.getTables());
377: resolveSelectableList(getValues(), db, _context
378: .getTables());
379: } else {
380: // resolve when condition
381: getWhenClause().resolve(db,
382: new TableIdentifier[] { getTable() });
383: resolveSelectableList(getValues(), db, getTable());
384: }
385:
386: assertRules(getTargetTable());
387: }
388:
389: private boolean _isTargetPartOfSubQuery = false;
390: }
391:
392: // -------------------------------------------------------------- Attributes
393:
394: private List _cols = new ArrayList();
395: private AxionQueryContext _context;
396: private RowDecorator _dec;
397:
398: private int _count;
399: private ExceptionWhenClause _exceptionWhenClause;
400: private AxionQueryPlanner _planner;
401: private boolean _resolved = false;
402: private boolean _isExternalDBTable = false;
403: private Table _table;
404:
405: private TableIdentifier _tableId;
406: private List _vals = new ArrayList();
407: private Selectable _where;
408: }
|