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.log;
007:
008: import java.sql.SQLException;
009:
010: import org.h2.constant.ErrorCode;
011: import org.h2.constant.SysProperties;
012: import org.h2.engine.Constants;
013: import org.h2.engine.Session;
014: import org.h2.index.Cursor;
015: import org.h2.index.Index;
016: import org.h2.message.Message;
017: import org.h2.result.Row;
018: import org.h2.store.DataPage;
019: import org.h2.store.FileStore;
020: import org.h2.table.Table;
021: import org.h2.util.ObjectArray;
022: import org.h2.value.Value;
023:
024: /**
025: * An entry in a undo log.
026: */
027: public class UndoLogRecord {
028: public static final short INSERT = 0, DELETE = 1;
029: private static final int IN_MEMORY = 0, STORED = 1,
030: IN_MEMORY_READ_POS = 2;
031: private Table table;
032: private Row row;
033: private short operation;
034: private short state;
035: private int filePos;
036:
037: public boolean isStored() {
038: return state == STORED;
039: }
040:
041: public boolean canStore() {
042: return table.getUniqueIndex() != null;
043: }
044:
045: public UndoLogRecord(Table table, short op, Row row) {
046: this .table = table;
047: this .row = row;
048: this .operation = op;
049: this .state = IN_MEMORY;
050: }
051:
052: public void undo(Session session) throws SQLException {
053: switch (operation) {
054: case INSERT:
055: if (state == IN_MEMORY_READ_POS) {
056: Index index = table.getUniqueIndex();
057: Cursor cursor = index.find(session, row, row);
058: cursor.next();
059: int pos = cursor.getPos();
060: row.setPos(pos);
061: state = IN_MEMORY;
062: }
063: if (session.getDatabase().getLockMode() == Constants.LOCK_MODE_OFF) {
064: if (row.getDeleted()) {
065: // it might have been deleted by another thread
066: return;
067: }
068: }
069: try {
070: table.removeRow(session, row);
071: } catch (SQLException e) {
072: if (session.getDatabase().getLockMode() == Constants.LOCK_MODE_OFF
073: && e.getErrorCode() == ErrorCode.ROW_NOT_FOUND_WHEN_DELETING_1) {
074: // it might have been deleted by another thread
075: // ignore
076: } else {
077: throw e;
078: }
079: }
080: break;
081: case DELETE:
082: try {
083: row.setPos(0);
084: table.addRow(session, row);
085: } catch (SQLException e) {
086: if (session.getDatabase().getLockMode() == Constants.LOCK_MODE_OFF
087: && e.getErrorCode() == ErrorCode.DUPLICATE_KEY_1) {
088: // it might have been added by another thread
089: // ignore
090: } else {
091: throw e;
092: }
093: }
094: break;
095: default:
096: throw Message.getInternalError("op=" + operation);
097: }
098: }
099:
100: public void save(DataPage buff, FileStore file) throws SQLException {
101: buff.reset();
102: buff.writeInt(0);
103: buff.writeInt(operation);
104: buff.writeInt(row.getColumnCount());
105: for (int i = 0; i < row.getColumnCount(); i++) {
106: buff.writeValue(row.getValue(i));
107: }
108: buff.fillAligned();
109: buff.setInt(0, buff.length() / Constants.FILE_BLOCK_SIZE);
110: buff.updateChecksum();
111: filePos = (int) (file.getFilePointer() / Constants.FILE_BLOCK_SIZE);
112: file.write(buff.getBytes(), 0, buff.length());
113: row = null;
114: state = STORED;
115: }
116:
117: public void seek(FileStore file) throws SQLException {
118: file.seek(filePos * Constants.FILE_BLOCK_SIZE);
119: }
120:
121: public void load(DataPage buff, FileStore file, Session session)
122: throws SQLException {
123: int min = Constants.FILE_BLOCK_SIZE;
124: seek(file);
125: buff.reset();
126: file.readFully(buff.getBytes(), 0, min);
127: int len = buff.readInt() * Constants.FILE_BLOCK_SIZE;
128: buff.checkCapacity(len);
129: if (len - min > 0) {
130: file.readFully(buff.getBytes(), min, len - min);
131: }
132: buff.check(len);
133: int op = buff.readInt();
134: if (SysProperties.CHECK) {
135: if (operation != op) {
136: throw Message.getInternalError("operation=" + operation
137: + " op=" + op);
138: }
139: }
140: int columnCount = buff.readInt();
141: Value[] values = new Value[columnCount];
142: for (int i = 0; i < columnCount; i++) {
143: values[i] = buff.readValue();
144: }
145: row = new Row(values, 0);
146: state = IN_MEMORY_READ_POS;
147: }
148:
149: public Table getTable() {
150: return table;
151: }
152:
153: public void commit() throws SQLException {
154: ObjectArray list = table.getIndexes();
155: for (int i = 0; i < list.size(); i++) {
156: Index index = (Index) list.get(i);
157: index.commit(operation, row);
158: }
159: }
160:
161: public Row getRow() {
162: return row;
163: }
164: }
|