001: /* Copyright (c) 2001-2005, The HSQL Development Group
002: * All rights reserved.
003: *
004: * Redistribution and use in source and binary forms, with or without
005: * modification, are permitted provided that the following conditions are met:
006: *
007: * Redistributions of source code must retain the above copyright notice, this
008: * list of conditions and the following disclaimer.
009: *
010: * Redistributions in binary form must reproduce the above copyright notice,
011: * this list of conditions and the following disclaimer in the documentation
012: * and/or other materials provided with the distribution.
013: *
014: * Neither the name of the HSQL Development Group nor the names of its
015: * contributors may be used to endorse or promote products derived from this
016: * software without specific prior written permission.
017: *
018: * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
019: * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
020: * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
021: * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
022: * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
023: * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
024: * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
025: * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
026: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
027: * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
028: * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
029: */
030:
031: package org.hsqldb;
032:
033: import org.hsqldb.lib.DoubleIntIndex;
034: import org.hsqldb.lib.HashMappedList;
035: import org.hsqldb.lib.HsqlArrayList;
036: import org.hsqldb.lib.LongKeyIntValueHashMap;
037:
038: /**
039: * Manages rows involved in transactions
040: *
041: * @author fredt@users
042: * @version 1.8.0
043: * @since 1.8.0
044: */
045: public class TransactionManager {
046:
047: LongKeyIntValueHashMap rowSessionMap;
048: boolean reWriteProtect;
049: Database database;
050:
051: TransactionManager(Database db) {
052: database = db;
053: rowSessionMap = new LongKeyIntValueHashMap(true);
054: }
055:
056: public void setReWriteProtection(boolean value) {
057: reWriteProtect = value;
058: }
059:
060: void checkDelete(Session session, Row row) throws HsqlException {
061: }
062:
063: void checkDelete(Session session, HashMappedList rowSet)
064: throws HsqlException {
065:
066: if (!reWriteProtect) {
067: return;
068: }
069:
070: int sessionid = session.getId();
071:
072: for (int i = 0, size = rowSet.size(); i < size; i++) {
073: Row row = (Row) rowSet.getKey(i);
074: long rowid = row.getId();
075:
076: if (rowSessionMap.get(rowid, sessionid) != sessionid) {
077: throw Trace.error(
078: Trace.INVALID_TRANSACTION_STATE_NO_SUBCLASS,
079: Trace.ITSNS_OVERWRITE);
080: }
081: }
082: }
083:
084: void checkDelete(Session session, HsqlArrayList rowSet)
085: throws HsqlException {
086:
087: if (!reWriteProtect) {
088: return;
089: }
090:
091: int sessionid = session.getId();
092:
093: for (int i = 0, size = rowSet.size(); i < size; i++) {
094: Row row = (Row) rowSet.get(i);
095: long rowid = row.getId();
096:
097: if (rowSessionMap.get(rowid, sessionid) != sessionid) {
098: throw Trace.error(
099: Trace.INVALID_TRANSACTION_STATE_NO_SUBCLASS,
100: Trace.ITSNS_OVERWRITE);
101: }
102: }
103: }
104:
105: void commit(Session session) {
106:
107: Object[] list = session.rowActionList.getArray();
108: int size = session.rowActionList.size();
109:
110: for (int i = 0; i < size; i++) {
111: Transaction tx = (Transaction) list[i];
112: long rowid = tx.row.getId();
113:
114: tx.commit(session);
115: rowSessionMap.remove(rowid);
116: }
117:
118: session.rowActionList.clear();
119: session.savepoints.clear();
120: }
121:
122: synchronized void rollback(Session session) {
123: rollbackTransactions(session, 0, false);
124: session.savepoints.clear();
125: }
126:
127: void rollbackSavepoint(Session session, String name)
128: throws HsqlException {
129:
130: int index = session.savepoints.getIndex(name);
131:
132: if (index < 0) {
133: throw Trace.error(Trace.SAVEPOINT_NOT_FOUND, name);
134: }
135:
136: Integer oi = (Integer) session.savepoints.get(index);
137: int limit = oi.intValue();
138:
139: rollbackTransactions(session, limit, false);
140:
141: while (session.savepoints.size() > index) {
142: session.savepoints.remove(session.savepoints.size() - 1);
143: }
144: }
145:
146: void rollbackTransactions(Session session, int limit, boolean log) {
147:
148: Object[] list = session.rowActionList.getArray();
149: int size = session.rowActionList.size();
150:
151: for (int i = size - 1; i >= limit; i--) {
152: Transaction tx = (Transaction) list[i];
153:
154: tx.rollback(session, log);
155: }
156:
157: for (int i = limit; i < size; i++) {
158: Transaction tx = (Transaction) list[i];
159: long rowid = tx.row.getId();
160:
161: rowSessionMap.remove(rowid);
162: }
163:
164: session.rowActionList.setSize(limit);
165: }
166:
167: void addTransaction(Session session, Transaction transaction) {
168:
169: if (reWriteProtect) {
170: rowSessionMap.put(transaction.row.getId(), session.getId());
171: }
172: }
173:
174: private long globalActionTimestamp = 0;
175:
176: /**
177: * gets the next timestamp for an action
178: */
179: long nextActionTimestamp() {
180:
181: globalActionTimestamp++;
182:
183: return globalActionTimestamp;
184: }
185:
186: /**
187: * Return an array of all transactions sorted by System Change No.
188: */
189: Transaction[] getTransactionList() {
190:
191: Session[] sessions = database.sessionManager.getAllSessions();
192: int[] tIndex = new int[sessions.length];
193: Transaction[] transactions;
194: int transactionCount = 0;
195:
196: {
197: int actioncount = 0;
198:
199: for (int i = 0; i < sessions.length; i++) {
200: actioncount += sessions[i].getTransactionSize();
201: }
202:
203: transactions = new Transaction[actioncount];
204: }
205:
206: while (true) {
207: boolean found = false;
208: long minChangeNo = Long.MAX_VALUE;
209: int sessionIndex = 0;
210:
211: // find the lowest available SCN across all sessions
212: for (int i = 0; i < sessions.length; i++) {
213: int tSize = sessions[i].getTransactionSize();
214:
215: if (tIndex[i] < tSize) {
216: Transaction current = (Transaction) sessions[i].rowActionList
217: .get(tIndex[i]);
218:
219: if (current.SCN < minChangeNo) {
220: minChangeNo = current.SCN;
221: sessionIndex = i;
222: }
223:
224: found = true;
225: }
226: }
227:
228: if (!found) {
229: break;
230: }
231:
232: HsqlArrayList currentList = sessions[sessionIndex].rowActionList;
233:
234: for (; tIndex[sessionIndex] < currentList.size();) {
235: Transaction current = (Transaction) currentList
236: .get(tIndex[sessionIndex]);
237:
238: // if the next change no is in this session, continue adding
239: if (current.SCN == minChangeNo + 1) {
240: minChangeNo++;
241: }
242:
243: if (current.SCN == minChangeNo) {
244: transactions[transactionCount++] = current;
245:
246: tIndex[sessionIndex]++;
247: } else {
248: break;
249: }
250: }
251: }
252:
253: return transactions;
254: }
255:
256: /**
257: * Return a lookup of all transactions ids for cached tables.
258: */
259: public DoubleIntIndex getTransactionIDList() {
260:
261: Session[] sessions = database.sessionManager.getAllSessions();
262: DoubleIntIndex lookup = new DoubleIntIndex(10, false);
263:
264: lookup.setKeysSearchTarget();
265:
266: for (int i = 0; i < sessions.length; i++) {
267: HsqlArrayList tlist = sessions[i].rowActionList;
268:
269: for (int j = 0, size = tlist.size(); j < size; j++) {
270: Transaction tx = (Transaction) tlist.get(j);
271:
272: if (tx.tTable.getTableType() == Table.CACHED_TABLE) {
273: lookup.addUnique(tx.row.getPos(), 0);
274: }
275: }
276: }
277:
278: return lookup;
279: }
280:
281: /**
282: * Convert row ID's for cached table rows in transactions
283: */
284: public void convertTransactionIDs(DoubleIntIndex lookup) {
285:
286: Session[] sessions = database.sessionManager.getAllSessions();
287:
288: for (int i = 0; i < sessions.length; i++) {
289: HsqlArrayList tlist = sessions[i].rowActionList;
290:
291: for (int j = 0, size = tlist.size(); j < size; j++) {
292: Transaction tx = (Transaction) tlist.get(j);
293:
294: if (tx.tTable.getTableType() == Table.CACHED_TABLE) {
295: int pos = lookup.lookupFirstEqual(tx.row.getPos());
296:
297: tx.row.setPos(pos);
298: }
299: }
300: }
301: }
302: }
|