001: /*
002: * $Id: TransactableTableImpl.java,v 1.90 2005/12/22 09:02:29 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;
042:
043: import java.io.File;
044: import java.util.Iterator;
045: import java.util.List;
046:
047: import org.apache.commons.collections.primitives.IntCollection;
048: import org.apache.commons.collections.primitives.IntIterator;
049: import org.axiondb.AxionException;
050: import org.axiondb.Column;
051: import org.axiondb.ColumnIdentifier;
052: import org.axiondb.Constraint;
053: import org.axiondb.Function;
054: import org.axiondb.Index;
055: import org.axiondb.Literal;
056: import org.axiondb.Row;
057: import org.axiondb.RowCollection;
058: import org.axiondb.RowComparator;
059: import org.axiondb.RowDecorator;
060: import org.axiondb.RowIterator;
061: import org.axiondb.RowSource;
062: import org.axiondb.Selectable;
063: import org.axiondb.Sequence;
064: import org.axiondb.Table;
065: import org.axiondb.TransactableTable;
066: import org.axiondb.Transaction;
067: import org.axiondb.engine.rowcollection.IntRowMap;
068: import org.axiondb.engine.rowcollection.IntSet;
069: import org.axiondb.engine.rowiterators.AbstractAcceptingRowIterator;
070: import org.axiondb.engine.rowiterators.ChainedRowIterator;
071: import org.axiondb.engine.rowiterators.CollatingRowIterator;
072: import org.axiondb.engine.rowiterators.DelegatingRowIterator;
073: import org.axiondb.engine.rowiterators.LazyRowRowIterator;
074: import org.axiondb.engine.rowiterators.TransformingRowIterator;
075: import org.axiondb.engine.rowiterators.UnmodifiableRowIterator;
076: import org.axiondb.engine.tables.AbstractBaseTable;
077: import org.axiondb.event.RowDeletedEvent;
078: import org.axiondb.event.RowEvent;
079: import org.axiondb.event.RowInsertedEvent;
080: import org.axiondb.event.RowUpdatedEvent;
081: import org.axiondb.functions.ComparisonFunction;
082:
083: /**
084: * An implemenation of {@link TransactableTable}.
085: *
086: * @version $Revision: 1.90 $ $Date: 2005/12/22 09:02:29 $
087: * @author Rodney Waldhoff
088: * @author Chuck Burdick
089: * @author Ahimanikya Satapathy
090: */
091: public final class TransactableTableImpl extends AbstractBaseTable
092: implements TransactableTable {
093:
094: public TransactableTableImpl(Table table) {
095: _table = table;
096: }
097:
098: public final String getName() {
099: return _table.getName();
100: }
101:
102: public final Table getTable() {
103: return _table;
104: }
105:
106: public final String getType() {
107: return _table.getType();
108: }
109:
110: public final RowDecorator makeRowDecorator() {
111: if (_dec == null) {
112: _dec = _table.makeRowDecorator();
113: }
114: return _dec;
115: }
116:
117: public void addConstraint(Constraint constraint)
118: throws AxionException {
119: _table.addConstraint(constraint);
120: }
121:
122: public Constraint removeConstraint(String name) {
123: return _table.removeConstraint(name);
124: }
125:
126: public final Constraint getConstraint(String name) {
127: return _table.getConstraint(name);
128: }
129:
130: public final Iterator getConstraints() {
131: return _table.getConstraints();
132: }
133:
134: /**
135: * Check if unique constraint exists on a column
136: *
137: * @param columnName name of the columm
138: * @return true if uniqueConstraint exists on the column
139: */
140: public boolean isUniqueConstraintExists(String columnName) {
141: return _table.isUniqueConstraintExists(columnName);
142: }
143:
144: /**
145: * Check if primary constraint exists on a column
146: *
147: * @param ColumnName name of the column
148: * @return if PrimaryKeyConstraint exists on the column
149: */
150: public boolean isPrimaryKeyConstraintExists(String columnName) {
151: return _table.isPrimaryKeyConstraintExists(columnName);
152: }
153:
154: public void addIndex(Index index) throws AxionException {
155: _table.addIndex(index);
156: }
157:
158: public void removeIndex(Index index) throws AxionException {
159: _table.removeIndex(index);
160: _insertedRows.clearIndexes();
161: _updatedRows.clearIndexes();
162: }
163:
164: public final boolean hasIndex(String name) throws AxionException {
165: return _table.hasIndex(name);
166: }
167:
168: public final void populateIndex(Index index) throws AxionException {
169: _table.populateIndex(index);
170: }
171:
172: public final Index getIndexForColumn(Column column) {
173: return _table.getIndexForColumn(column);
174: }
175:
176: public final boolean isColumnIndexed(Column column) {
177: return _table.isColumnIndexed(column);
178: }
179:
180: public void addColumn(Column col) throws AxionException {
181: _table.addColumn(col);
182: }
183:
184: public final Column getColumn(int index) {
185: return _table.getColumn(index);
186: }
187:
188: public final Column getColumn(String name) {
189: return _table.getColumn(name);
190: }
191:
192: public final boolean hasColumn(ColumnIdentifier id) {
193: return _table.hasColumn(id);
194: }
195:
196: public final int getColumnIndex(String name) throws AxionException {
197: return _table.getColumnIndex(name);
198: }
199:
200: public final List getColumnIdentifiers() {
201: return _table.getColumnIdentifiers();
202: }
203:
204: public final int getColumnCount() {
205: return _table.getColumnCount();
206: }
207:
208: public final Iterator getIndices() {
209: return _table.getIndices();
210: }
211:
212: public void addRow(Row row) throws AxionException {
213: int rowid = _table.getNextRowId();
214: row.setIdentifier(rowid);
215: RowEvent event = new RowInsertedEvent(this , null, row);
216: try {
217: checkConstraints(event, makeRowDecorator());
218: } catch (AxionException e) {
219: freeRowId(rowid);
220: throw e;
221: }
222: _insertedRows.addRow(this , row);
223: publishEvent(event);
224: }
225:
226: public RowIterator getRowIterator(boolean readOnly)
227: throws AxionException {
228: if ((!readOnly) || hasUpdates() || hasDeletes() || hasInserts()) {
229: ChainedRowIterator chain = new ChainedRowIterator();
230: chain.addRowIterator(excludeDeletedTransformUpdated(_table
231: .getRowIterator(readOnly)));
232: chain.addRowIterator(new InsertedRowIterator(_insertedRows
233: .rowIterator()));
234: return chain;
235: }
236: return UnmodifiableRowIterator.wrap(_table
237: .getRowIterator(readOnly));
238: }
239:
240: public RowIterator getIndexedRows(Selectable node, boolean readOnly)
241: throws AxionException {
242: return getIndexedRows(this .getTable(), node, readOnly);
243: }
244:
245: public RowIterator getIndexedRows(RowSource source,
246: Selectable node, boolean readOnly) throws AxionException {
247: RowIterator rows = _table
248: .getIndexedRows(source, node, readOnly);
249: if (null != rows) {
250: if ((!readOnly) || hasUpdates() || hasDeletes()
251: || hasInserts()) {
252: // Ensure rows in transaction are returned in natural order.
253: // CollatingRowIterator takes two ordered row iterators and
254: // collates their rows
255: Selectable col = getIndexColumn(node);
256: RowComparator comparator = new RowComparator(col,
257: makeRowDecorator());
258: CollatingRowIterator collator = new CollatingRowIterator(
259: comparator);
260: collator.addRowIterator(excludeDeletedAndUpdated(rows));
261:
262: Column column = this .getColumn(col.getName());
263: Index basendx = getIndexForColumn(column);
264: addUpdatedRowIterator(basendx, node, collator, readOnly);
265: addInsertedRowIterator(basendx, node, collator,
266: readOnly);
267: rows = collator;
268: } else {
269: rows = UnmodifiableRowIterator.wrap(rows);
270: }
271: }
272: return rows;
273: }
274:
275: private Selectable getIndexColumn(Selectable node) {
276: if (node instanceof ColumnIdentifier) {
277: return node;
278: } else if (node instanceof ComparisonFunction) {
279: ComparisonFunction fn = (ComparisonFunction) node;
280: Selectable left = fn.getArgument(0);
281: Selectable right = fn.getArgument(1);
282: if (left instanceof ColumnIdentifier
283: && right instanceof Literal) {
284: return left;
285: } else if (left instanceof Literal
286: && right instanceof ColumnIdentifier) {
287: return right;
288: }
289: } else if (node instanceof Function) {
290: Function fn = (Function) node;
291: if (fn.getArgumentCount() == 1) {
292: Selectable column = fn.getArgument(0);
293: if (column instanceof ColumnIdentifier) {
294: return column;
295: }
296: }
297: }
298: return null;
299: }
300:
301: private void addUpdatedRowIterator(Index basendx, Selectable node,
302: CollatingRowIterator collator, boolean readOnly)
303: throws AxionException {
304: makeIndexForRowsInTransaction(basendx, _updatedRows);
305: collator.addRowIterator(_updatedRows.getIndexedRows(this , node,
306: readOnly));
307: }
308:
309: private void addInsertedRowIterator(Index basendx, Selectable node,
310: CollatingRowIterator collator, boolean readOnly)
311: throws AxionException {
312: makeIndexForRowsInTransaction(basendx, _insertedRows);
313: collator.addRowIterator(new InsertedRowIterator(_insertedRows
314: .getIndexedRows(this , node, readOnly)));
315: }
316:
317: private void makeIndexForRowsInTransaction(Index basendx,
318: IntRowMap rowMap) throws AxionException {
319: if (rowMap.getIndexForColumn(basendx.getIndexedColumn()) == null) {
320: Index index = null;
321: if (basendx.getType().equals(Index.ARRAY)) {
322: index = ARRAY_INDEX_FACTORY.makeNewInstance(basendx
323: .getName(), basendx.getIndexedColumn(), false,
324: true);
325: } else {
326: index = BTREE_INDEX_FACTORY.makeNewInstance(basendx
327: .getName(), basendx.getIndexedColumn(), false,
328: true);
329: }
330: rowMap.addIndex(index);
331: rowMap.populateIndex(this , index);
332: }
333: }
334:
335: public final int getRowCount() {
336: return _table.getRowCount() + _insertedRows.size()
337: - _deletedRows.size();
338: }
339:
340: public final int getNextRowId() {
341: return _table.getNextRowId();
342: }
343:
344: public final void freeRowId(int id) {
345: _table.freeRowId(id);
346: }
347:
348: public void drop() throws AxionException {
349: _table.drop();
350: _table = null;
351: _dec = null;
352: _insertedRows = null;
353: _updatedRows = null;
354: _deletedRows = null;
355: }
356:
357: public void checkpoint() throws AxionException {
358: _table.checkpoint();
359: }
360:
361: public void shutdown() throws AxionException {
362: if (hasInserts()) {
363: freeRowIds();
364: _insertedRows.shutdown();
365: }
366: if (hasUpdates()) {
367: _updatedRows.shutdown();
368: }
369: if (hasDeletes()) {
370: _deletedRows.clear();
371: }
372:
373: if (_dec != null) {
374: _dec = null;
375: }
376:
377: if (_table != null) {
378: _table.shutdown();
379: }
380: }
381:
382: public void setDeferAllConstraints(boolean deferAll) {
383: _deferAll = deferAll;
384: }
385:
386: public void setSequence(Sequence seq) throws AxionException {
387: _table.setSequence(seq);
388: }
389:
390: public final Sequence getSequence() {
391: return _table.getSequence();
392: }
393:
394: public void remount(File dir, boolean dataOnly)
395: throws AxionException {
396: _table.remount(dir, dataOnly);
397: }
398:
399: public void rename(String oldName, String newName)
400: throws AxionException {
401: _table.rename(oldName, newName);
402: }
403:
404: public Row getRow(int id) throws AxionException {
405: if (isDeleted(id)) {
406: return null;
407: }
408: Row row = _updatedRows.getRow(id);
409: if (null != row) {
410: return row;
411: }
412: row = getInsertedRow(id);
413: if (null != row) {
414: return row;
415: }
416: return _table.getRow(id);
417: }
418:
419: public final void applyInserts(RowCollection rows)
420: throws AxionException {
421: _table.applyInserts(rows);
422: }
423:
424: public final void applyDeletes(IntCollection rowids)
425: throws AxionException {
426: _table.applyDeletes(rowids);
427: }
428:
429: public final void applyUpdates(RowCollection rows)
430: throws AxionException {
431: _table.applyUpdates(rows);
432: }
433:
434: public void commit() throws AxionException {
435: assertOpen();
436: if (hasDeferredConstraint()) {
437: if (hasInserts()) {
438: checkConstraints(null, _insertedRows.rowIterator());
439: }
440: if (hasDeletes()) {
441: checkConstraints(new LazyRowRowIterator(_table,
442: _deletedRows.listIterator(), _deletedRows
443: .size()), null);
444: }
445: if (hasUpdates()) {
446: checkConstraints(
447: new LazyRowRowIterator(_table, _updatedRows
448: .keyIterator(), _updatedRows.size()),
449: _updatedRows.rowIterator());
450: }
451: }
452: _state = Transaction.STATE_COMMITTED;
453: }
454:
455: public void rollback() throws AxionException {
456: assertOpen();
457: freeRowIds();
458:
459: _table = null;
460: _dec = null;
461: _insertedRows = null;
462: _updatedRows = null;
463: _deletedRows = null;
464: _state = Transaction.STATE_ABORTED;
465: }
466:
467: private void freeRowIds() {
468: IntIterator itr = _insertedRows.keyIterator();
469: while (itr.hasNext()) {
470: freeRowId(itr.next());
471: }
472: }
473:
474: public void apply() throws AxionException {
475: assertCommitted();
476:
477: // apply deletes
478: if (!_deletedRows.isEmpty()) {
479: _table.applyDeletes(_deletedRows);
480: _deletedRows.clear();
481: }
482:
483: // apply updates
484: if (!_updatedRows.isEmpty()) {
485: _table.applyUpdates(_updatedRows.rowValues());
486: _updatedRows.clear();
487: }
488:
489: // apply inserts
490: if (!_insertedRows.isEmpty()) {
491: _table.applyInserts(_insertedRows.rowValues());
492: _insertedRows.clear();
493: }
494:
495: _state = Transaction.STATE_APPLIED;
496: }
497:
498: public final TransactableTable makeTransactableTable() {
499: return new TransactableTableImpl(this );
500: }
501:
502: public void deleteRow(Row row) throws AxionException {
503: // by construction, this method should never be called for a row that only exists
504: // in _insertedRows, so we'll ignore that case
505:
506: RowEvent event = new RowDeletedEvent(this , row, null);
507: checkConstraints(event, makeRowDecorator());
508:
509: // add the row to our list of deleted rows and
510: // delete from updated/inserted Rows, if it's in there
511: if (_deletedRows.add(row.getIdentifier())) {
512: _updatedRows.deleteRow(this , row);
513: //_insertedRows.deleteRow(this, row);
514: publishEvent(event);
515: }
516: }
517:
518: public void updateRow(Row oldrow, Row newrow) throws AxionException {
519: newrow.setIdentifier(oldrow.getIdentifier());
520: RowEvent event = new RowUpdatedEvent(this , oldrow, newrow);
521: checkConstraints(event, makeRowDecorator());
522: _updatedRows.updateRow(this , oldrow, newrow);
523: publishEvent(event);
524: }
525:
526: private final Row getInsertedRow(int id) {
527: return _insertedRows.getRow(id);
528: }
529:
530: private void assertOpen() throws AxionException {
531: if (Transaction.STATE_OPEN != _state) {
532: throw new AxionException(
533: "Already committed or rolled back [" + _state
534: + "].");
535: }
536: }
537:
538: private void assertCommitted() throws AxionException {
539: if (Transaction.STATE_COMMITTED != _state) {
540: throw new AxionException("Not committed [" + _state + "].");
541: }
542: }
543:
544: /**
545: * Overrides {@link #remove}and {@link #set}to apply them to the current
546: * transaction.
547: */
548: private class TransactableTableRowIterator extends
549: DelegatingRowIterator {
550: public TransactableTableRowIterator(RowIterator iter) {
551: super (iter);
552: }
553:
554: public void add(Row row) throws AxionException {
555: addRow(row);
556: }
557:
558: public void remove() throws AxionException {
559: deleteRow(current());
560: }
561:
562: public void set(Row row) throws AxionException {
563: updateRow(current(), row);
564: }
565: }
566:
567: /**
568: * Filters out rows that have been deleted in the current transaction.
569: */
570: private class ExcludeDeleted extends AbstractAcceptingRowIterator {
571: public ExcludeDeleted(RowIterator iter) {
572: super (iter);
573: }
574:
575: protected boolean acceptable(int rowindex, Row row)
576: throws AxionException {
577: return !isDeleted(row.getIdentifier());
578: }
579:
580: public int size() throws AxionException {
581: return getDelegate().size() - _deletedRows.size();
582: }
583: }
584:
585: private final boolean isDeleted(int rowid) {
586: return _deletedRows.contains(rowid);
587: }
588:
589: protected final boolean isDeferAll() {
590: return _deferAll;
591: }
592:
593: /**
594: * Filters out rows that have been updated in the current transaction.
595: */
596: private class ExcludeUpdated extends AbstractAcceptingRowIterator {
597:
598: public ExcludeUpdated(RowIterator iter) {
599: super (iter);
600: }
601:
602: protected boolean acceptable(int rowindex, Row row)
603: throws AxionException {
604: return !(_updatedRows.containsKey(row.getIdentifier()));
605: }
606:
607: public int size() throws AxionException {
608: return getDelegate().size() - _updatedRows.size();
609: }
610: }
611:
612: /**
613: * Transforms rows that have been updated within the current transaction.
614: */
615: private class TransformUpdated extends TransformingRowIterator {
616: public TransformUpdated(RowIterator iter) {
617: super (iter);
618: }
619:
620: protected Row transform(Row row) {
621: Row updated = _updatedRows.getRow(row.getIdentifier());
622: if (null != updated) {
623: return updated;
624: }
625: return row;
626: }
627: }
628:
629: private RowIterator excludeDeletedTransformUpdated(RowIterator base) {
630: if (null == base) {
631: return null;
632: }
633: return new TransactableTableRowIterator(new ExcludeDeleted(
634: new TransformUpdated(base)));
635: }
636:
637: private RowIterator excludeDeletedAndUpdated(RowIterator base) {
638: if (null == base) {
639: return null;
640: }
641: return new TransactableTableRowIterator(new ExcludeDeleted(
642: new ExcludeUpdated(base)));
643: }
644:
645: private class InsertedRowIterator extends DelegatingRowIterator {
646: public InsertedRowIterator(RowIterator iter) {
647: super (iter);
648: }
649:
650: public void add(Row row) throws AxionException {
651: addRow(row);
652: }
653:
654: public void remove() throws AxionException {
655: _table.freeRowId(current().getIdentifier());
656: super .remove();
657: }
658:
659: public void set(Row row) throws AxionException {
660: updateRow(current(), row);
661: }
662: }
663:
664: private final boolean hasUpdates() {
665: return !(_updatedRows == null || _updatedRows.isEmpty());
666: }
667:
668: private final boolean hasDeletes() {
669: return !(_deletedRows == null || _deletedRows.isEmpty());
670: }
671:
672: private final boolean hasInserts() {
673: return !(_insertedRows == null || _insertedRows.isEmpty());
674: }
675:
676: public void truncate() throws AxionException {
677: _insertedRows.clear();
678: _updatedRows.clear();
679: _deletedRows.clear();
680: _table.truncate();
681: }
682:
683: public String toString() {
684: StringBuffer buf = new StringBuffer(10);
685: buf.append("TransactableTable[");
686: buf.append("name=").append(_table.getName()).append(":");
687: buf.append("inserted=").append(_insertedRows.size())
688: .append(",");
689: buf.append("updated=").append(_updatedRows.size()).append(",");
690: buf.append("deleted=").append(_deletedRows.size()).append("]");
691: return buf.toString();
692: }
693:
694: private Table _table;
695: private RowDecorator _dec;
696: List _constraints;
697: List _indices;
698:
699: /** {@link IntRowMap}of {@link Row}s that have been inserted. */
700: private IntRowMap _insertedRows = new IntRowMap();
701:
702: /**
703: * {@link IntRowMap}of {@link Row}s that have been updated, keyed by row identifier.
704: */
705: private IntRowMap _updatedRows = new IntRowMap();
706:
707: /** {@link IntSet}of row identifiers that have been deleted. */
708: private IntSet _deletedRows = new IntSet();
709:
710: private static ArrayIndexFactory ARRAY_INDEX_FACTORY = new ArrayIndexFactory();
711: private static BTreeIndexFactory BTREE_INDEX_FACTORY = new BTreeIndexFactory();
712:
713: /** My current state. */
714: private int _state = Transaction.STATE_OPEN;
715: private boolean _deferAll = false;
716: }
|