File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\engine\UndoLogRecord.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.engine;
8
9 import org.h2.constant.ErrorCode;
10 import org.h2.constant.SysProperties;
11 import org.h2.message.DbException;
12 import org.h2.result.Row;
13 import org.h2.store.Data;
14 import org.h2.store.FileStore;
15 import org.h2.table.Table;
16 import org.h2.value.Value;
17
18 /**
19 * An entry in a undo log.
20 */
21 public class UndoLogRecord {
22
23 /**
24 * Operation type meaning the row was inserted.
25 */
26 public static final short INSERT = 0;
27
28 /**
29 * Operation type meaning the row was deleted.
30 */
31 public static final short DELETE = 1;
32
33 private static final int IN_MEMORY = 0, STORED = 1, IN_MEMORY_INVALID = 2;
34 private Table table;
35 private Row row;
36 private short operation;
37 private short state;
38 private int filePos;
39
40 /**
41 * Create a new undo log record
42 *
43 * @param table the table
44 * @param op the operation type
45 * @param row the row that was deleted or inserted
46 */
47 UndoLogRecord(Table table, short op, Row row) {
48 this.table = table;
49 this.row = row;
50 this.operation = op;
51 this.state = IN_MEMORY;
52 }
53
54 /**
55 * Check if the log record is stored in the file.
56 *
57 * @return true if it is
58 */
59 boolean isStored() {
60 return state == STORED;
61 }
62
63 /**
64 * Check if this undo log record can be store. Only record can be stored if
65 * the table has a unique index.
66 *
67 * @return if it can be stored
68 */
69 boolean canStore() {
70 // if large transactions are enabled, this method is not called
71 if (table.getUniqueIndex() != null) {
72 return true;
73 }
74 return false;
75 }
76
77 /**
78 * Un-do the operation. If the row was inserted before, it is deleted now,
79 * and vice versa.
80 *
81 * @param session the session
82 */
83 void undo(Session session) {
84 Database db = session.getDatabase();
85 switch (operation) {
86 case INSERT:
87 if (state == IN_MEMORY_INVALID) {
88 state = IN_MEMORY;
89 }
90 if (db.getLockMode() == Constants.LOCK_MODE_OFF) {
91 if (row.isDeleted()) {
92 // it might have been deleted by another thread
93 return;
94 }
95 }
96 try {
97 row.setDeleted(false);
98 table.removeRow(session, row);
99 table.fireAfterRow(session, row, null, true);
100 } catch (DbException e) {
101 if (session.getDatabase().getLockMode() == Constants.LOCK_MODE_OFF
102 && e.getErrorCode() == ErrorCode.ROW_NOT_FOUND_WHEN_DELETING_1) {
103 // it might have been deleted by another thread
104 // ignore
105 } else {
106 throw e;
107 }
108 }
109 break;
110 case DELETE:
111 try {
112 table.addRow(session, row);
113 table.fireAfterRow(session, null, row, true);
114 // reset session id, otherwise other session think
115 // that this row was inserted by this session
116 row.commit();
117 } catch (DbException e) {
118 if (session.getDatabase().getLockMode() == Constants.LOCK_MODE_OFF
119 && e.getSQLException().getErrorCode() == ErrorCode.DUPLICATE_KEY_1) {
120 // it might have been added by another thread
121 // ignore
122 } else {
123 throw e;
124 }
125 }
126 break;
127 default:
128 DbException.throwInternalError("op=" + operation);
129 }
130 }
131
132 /**
133 * Append the row to the buffer.
134 *
135 * @param buff the buffer
136 * @param log the undo log
137 */
138 void append(Data buff, UndoLog log) {
139 int p = buff.length();
140 buff.writeInt(0);
141 buff.writeInt(operation);
142 buff.writeByte(row.isDeleted() ? (byte) 1 : (byte) 0);
143 buff.writeInt(log.getTableId(table));
144 buff.writeLong(row.getKey());
145 buff.writeInt(row.getSessionId());
146 int count = row.getColumnCount();
147 buff.writeInt(count);
148 for (int i = 0; i < count; i++) {
149 Value v = row.getValue(i);
150 buff.checkCapacity(buff.getValueLen(v));
151 buff.writeValue(v);
152 }
153 buff.fillAligned();
154 buff.setInt(p, (buff.length() - p) / Constants.FILE_BLOCK_SIZE);
155 }
156
157 /**
158 * Save the row in the file using a buffer.
159 *
160 * @param buff the buffer
161 * @param file the file
162 * @param log the undo log
163 */
164 void save(Data buff, FileStore file, UndoLog log) {
165 buff.reset();
166 append(buff, log);
167 filePos = (int) (file.getFilePointer() / Constants.FILE_BLOCK_SIZE);
168 file.write(buff.getBytes(), 0, buff.length());
169 row = null;
170 state = STORED;
171 }
172
173 /**
174 * Load an undo log record row using a buffer.
175 *
176 * @param buff the buffer
177 * @param log the log
178 * @return the undo log record
179 */
180 static UndoLogRecord loadFromBuffer(Data buff, UndoLog log) {
181 UndoLogRecord rec = new UndoLogRecord(null, (short) 0, null);
182 int pos = buff.length();
183 int len = buff.readInt() * Constants.FILE_BLOCK_SIZE;
184 rec.load(buff, log);
185 buff.setPos(pos + len);
186 return rec;
187 }
188
189 /**
190 * Load an undo log record row using a buffer.
191 *
192 * @param buff the buffer
193 * @param file the source file
194 * @param log the log
195 */
196 void load(Data buff, FileStore file, UndoLog log) {
197 int min = Constants.FILE_BLOCK_SIZE;
198 log.seek(filePos);
199 buff.reset();
200 file.readFully(buff.getBytes(), 0, min);
201 int len = buff.readInt() * Constants.FILE_BLOCK_SIZE;
202 buff.checkCapacity(len);
203 if (len - min > 0) {
204 file.readFully(buff.getBytes(), min, len - min);
205 }
206 int oldOp = operation;
207 load(buff, log);
208 if (SysProperties.CHECK) {
209 if (operation != oldOp) {
210 DbException.throwInternalError("operation=" + operation + " op=" + oldOp);
211 }
212 }
213 }
214
215 private void load(Data buff, UndoLog log) {
216 operation = (short) buff.readInt();
217 boolean deleted = buff.readByte() == 1;
218 table = log.getTable(buff.readInt());
219 long key = buff.readLong();
220 int sessionId = buff.readInt();
221 int columnCount = buff.readInt();
222 Value[] values = new Value[columnCount];
223 for (int i = 0; i < columnCount; i++) {
224 values[i] = buff.readValue();
225 }
226 row = new Row(values, Row.MEMORY_CALCULATE);
227 row.setKey(key);
228 row.setDeleted(deleted);
229 row.setSessionId(sessionId);
230 state = IN_MEMORY_INVALID;
231 }
232
233 /**
234 * Get the table.
235 *
236 * @return the table
237 */
238 public Table getTable() {
239 return table;
240 }
241
242 /**
243 * Get the position in the file.
244 *
245 * @return the file position
246 */
247 public long getFilePos() {
248 return filePos;
249 }
250
251 /**
252 * This method is called after the operation was committed.
253 * It commits the change to the indexes.
254 */
255 void commit() {
256 table.commit(operation, row);
257 }
258
259 /**
260 * Get the row that was deleted or inserted.
261 *
262 * @return the row
263 */
264 public Row getRow() {
265 return row;
266 }
267
268 /**
269 * Change the state from IN_MEMORY to IN_MEMORY_INVALID. This method is
270 * called if a later record was read from the temporary file, and therefore
271 * the position could have changed.
272 */
273 void invalidatePos() {
274 if (this.state == IN_MEMORY) {
275 state = IN_MEMORY_INVALID;
276 }
277 }
278 }
279