File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\engine\UndoLog.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 java.util.ArrayList;
10 import java.util.HashMap;
11 import org.h2.constant.SysProperties;
12 import org.h2.message.DbException;
13 import org.h2.store.Data;
14 import org.h2.store.FileStore;
15 import org.h2.table.Table;
16 import org.h2.util.New;
17
18 /**
19 * Each session keeps a undo log if rollback is required.
20 */
21 public class UndoLog {
22
23 private final Database database;
24 private final ArrayList<Long> storedEntriesPos = New.arrayList();
25 private final ArrayList<UndoLogRecord> records = New.arrayList();
26 private FileStore file;
27 private Data rowBuff;
28 private int memoryUndo;
29 private int storedEntries;
30 private HashMap<Integer, Table> tables;
31 private final boolean largeTransactions;
32
33 /**
34 * Create a new undo log for the given session.
35 *
36 * @param session the session
37 */
38 UndoLog(Session session) {
39 this.database = session.getDatabase();
40 largeTransactions = database.getSettings().largeTransactions;
41 }
42
43 /**
44 * Get the number of active rows in this undo log.
45 *
46 * @return the number of rows
47 */
48 int size() {
49 if (largeTransactions) {
50 return storedEntries + records.size();
51 }
52 if (SysProperties.CHECK && memoryUndo > records.size()) {
53 DbException.throwInternalError();
54 }
55 return records.size();
56 }
57
58 /**
59 * Clear the undo log. This method is called after the transaction is
60 * committed.
61 */
62 void clear() {
63 records.clear();
64 storedEntries = 0;
65 storedEntriesPos.clear();
66 memoryUndo = 0;
67 if (file != null) {
68 file.closeAndDeleteSilently();
69 file = null;
70 rowBuff = null;
71 }
72 }
73
74 /**
75 * Get the last record and remove it from the list of operations.
76 *
77 * @return the last record
78 */
79 public UndoLogRecord getLast() {
80 int i = records.size() - 1;
81 if (largeTransactions) {
82 if (i < 0 && storedEntries > 0) {
83 int last = storedEntriesPos.size() - 1;
84 long pos = storedEntriesPos.get(last);
85 storedEntriesPos.remove(last);
86 long end = file.length();
87 int bufferLength = (int) (end - pos);
88 Data buff = Data.create(database, bufferLength);
89 file.seek(pos);
90 file.readFully(buff.getBytes(), 0, bufferLength);
91 while (buff.length() < bufferLength) {
92 UndoLogRecord e = UndoLogRecord.loadFromBuffer(buff, this);
93 records.add(e);
94 memoryUndo++;
95 }
96 storedEntries -= records.size();
97 file.setLength(pos);
98 file.seek(pos);
99 }
100 i = records.size() - 1;
101 }
102 UndoLogRecord entry = records.get(i);
103 if (entry.isStored()) {
104 int start = Math.max(0, i - database.getMaxMemoryUndo() / 2);
105 UndoLogRecord first = null;
106 for (int j = start; j <= i; j++) {
107 UndoLogRecord e = records.get(j);
108 if (e.isStored()) {
109 e.load(rowBuff, file, this);
110 memoryUndo++;
111 if (first == null) {
112 first = e;
113 }
114 }
115 }
116 for (int k = 0; k < i; k++) {
117 UndoLogRecord e = records.get(k);
118 e.invalidatePos();
119 }
120 seek(first.getFilePos());
121 }
122 return entry;
123 }
124
125 /**
126 * Go to the right position in the file.
127 *
128 * @param filePos the position in the file
129 */
130 void seek(long filePos) {
131 file.seek(filePos * Constants.FILE_BLOCK_SIZE);
132 }
133
134 /**
135 * Remove the last record from the list of operations.
136 *
137 * @param trimToSize if the undo array should shrink to conserve memory
138 */
139 void removeLast(boolean trimToSize) {
140 int i = records.size() - 1;
141 UndoLogRecord r = records.remove(i);
142 if (!r.isStored()) {
143 memoryUndo--;
144 }
145 if (trimToSize && i > 1024 && (i & 1023) == 0) {
146 records.trimToSize();
147 }
148 }
149
150 /**
151 * Append an undo log entry to the log.
152 *
153 * @param entry the entry
154 */
155 void add(UndoLogRecord entry) {
156 records.add(entry);
157 if (largeTransactions) {
158 memoryUndo++;
159 if (memoryUndo > database.getMaxMemoryUndo() && database.isPersistent() && !database.isMultiVersion()) {
160 if (file == null) {
161 String fileName = database.createTempFile();
162 file = database.openFile(fileName, "rw", false);
163 file.setCheckedWriting(false);
164 file.setLength(FileStore.HEADER_LENGTH);
165 }
166 Data buff = Data.create(database, Constants.DEFAULT_PAGE_SIZE);
167 for (int i = 0; i < records.size(); i++) {
168 UndoLogRecord r = records.get(i);
169 buff.checkCapacity(Constants.DEFAULT_PAGE_SIZE);
170 r.append(buff, this);
171 if (i == records.size() - 1 || buff.length() > Constants.UNDO_BLOCK_SIZE) {
172 storedEntriesPos.add(file.getFilePointer());
173 file.write(buff.getBytes(), 0, buff.length());
174 buff.reset();
175 }
176 }
177 storedEntries += records.size();
178 memoryUndo = 0;
179 records.clear();
180 file.autoDelete();
181 return;
182 }
183 } else {
184 if (!entry.isStored()) {
185 memoryUndo++;
186 }
187 if (memoryUndo > database.getMaxMemoryUndo() && database.isPersistent() && !database.isMultiVersion()) {
188 if (file == null) {
189 String fileName = database.createTempFile();
190 file = database.openFile(fileName, "rw", false);
191 file.setCheckedWriting(false);
192 file.seek(FileStore.HEADER_LENGTH);
193 rowBuff = Data.create(database, Constants.DEFAULT_PAGE_SIZE);
194 Data buff = rowBuff;
195 for (int i = 0; i < records.size(); i++) {
196 UndoLogRecord r = records.get(i);
197 saveIfPossible(r, buff);
198 }
199 } else {
200 saveIfPossible(entry, rowBuff);
201 }
202 file.autoDelete();
203 }
204 }
205 }
206
207 private void saveIfPossible(UndoLogRecord r, Data buff) {
208 if (!r.isStored() && r.canStore()) {
209 r.save(buff, file, this);
210 memoryUndo--;
211 }
212 }
213
214 /**
215 * Get the table id for this undo log. If the table is not registered yet,
216 * this is done as well.
217 *
218 * @param table the table
219 * @return the id
220 */
221 int getTableId(Table table) {
222 int id = table.getId();
223 if (tables == null) {
224 tables = New.hashMap();
225 }
226 // need to overwrite the old entry, because the old object
227 // might be deleted in the meantime
228 tables.put(id, table);
229 return id;
230 }
231
232 /**
233 * Get the table for this id. The table must be registered for this undo log
234 * first by calling getTableId.
235 *
236 * @param id the table id
237 * @return the table object
238 */
239 Table getTable(int id) {
240 return tables.get(id);
241 }
242
243 }
244