KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > mckoi > database > MasterTableJournal


1 /**
2  * com.mckoi.database.MasterTableJournal 19 Nov 2000
3  *
4  * Mckoi SQL Database ( http://www.mckoi.com/database )
5  * Copyright (C) 2000, 2001, 2002 Diehl and Associates, Inc.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * Version 2 as published by the Free Software Foundation.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License Version 2 for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * Version 2 along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19  *
20  * Change Log:
21  *
22  *
23  */

24
25 package com.mckoi.database;
26
27 import com.mckoi.util.IntegerVector;
28 import java.io.*;
29
30 /**
31  * A journal of changes that occured to a table in a data conglomerate during
32  * a transaction.
33  *
34  * @author Tobias Downer
35  */

36
37 final class MasterTableJournal {
38
39   /**
40    * Journal commands.
41    */

42   final static byte TABLE_ADD = 1; // Add a row to a table.
43
// (params: table_id, row_index)
44
final static byte TABLE_REMOVE = 2; // Remove a row from a table.
45
// (params: table_id, row_index)
46
final static byte TABLE_UPDATE_ADD = 5; // Add a row from an update.
47
final static byte TABLE_UPDATE_REMOVE = 6; // Remove a row from an update.
48

49   /**
50    * The commit id given to this change when it is committed. This is only
51    * set when the journal is a committed change to the database.
52    */

53   private long commit_id;
54
55
56   /**
57    * The master table id.
58    */

59   private int table_id;
60
61   /**
62    * The number of entries in this journal.
63    */

64   private int journal_entries;
65
66   /**
67    * A byte[] array that represents the set of commands a transaction
68    * performed on this table.
69    */

70   private byte[] command_journal;
71
72   /**
73    * An IntegerVector that is filled with parameters from the command journal.
74    * For example, a 'TABLE_ADD' journal log will have as parameters the
75    * row_index that was added to this table.
76    */

77   private IntegerVector command_parameters;
78
79   /**
80    * Constructs the master table journal.
81    */

82   MasterTableJournal(int table_id) {
83     this.table_id = table_id;
84     command_journal = new byte[16];
85     command_parameters = new IntegerVector(32);
86   }
87
88   MasterTableJournal() {
89     this(-1);
90   }
91
92   /**
93    * Sets the 'commit_id'. This is only set when this change becomes a
94    * committed change to the database.
95    */

96   void setCommitID(long commit_id) {
97     this.commit_id = commit_id;
98   }
99
100   /**
101    * Returns true if the given command is an addition command.
102    */

103   static boolean isAddCommand(byte command) {
104     return ((command & 0x03) == TABLE_ADD);
105   }
106
107   /**
108    * Returns true if the given command is a removal command.
109    */

110   static boolean isRemoveCommand(byte command) {
111     return ((command & 0x03) == TABLE_REMOVE);
112   }
113   
114   /**
115    * Adds a command to the journal.
116    */

117   private void addCommand(byte command) {
118     if (journal_entries >= command_journal.length) {
119       // Resize command array.
120
int grow_size = Math.min(4000, journal_entries);
121       grow_size = Math.max(4, grow_size);
122       byte[] new_command_journal = new byte[journal_entries + grow_size];
123       System.arraycopy(command_journal, 0, new_command_journal, 0,
124                        journal_entries);
125       command_journal = new_command_journal;
126     }
127
128     command_journal[journal_entries] = command;
129     ++journal_entries;
130   }
131
132   /**
133    * Adds a parameter to the journal command parameters.
134    */

135   private void addParameter(int param) {
136     command_parameters.addInt(param);
137   }
138
139   /**
140    * Removes the top n entries from the journal.
141    */

142   private void removeTopEntries(int n) {
143     journal_entries = journal_entries - n;
144     command_parameters.crop(0, command_parameters.size() - n);
145   }
146   
147   /**
148    * Adds a new command to this journal.
149    */

150   void addEntry(byte command, int row_index) {
151     addCommand(command);
152     addParameter(row_index);
153   }
154
155   // ---------- Getters ----------
156
// These methods assume the journal has been setup and no more entries
157
// will be made.
158

159   /**
160    * Returns the commit_id that has been set for this journal.
161    */

162   long getCommitID() {
163     return commit_id;
164   }
165
166   /**
167    * Returns the table id of the master table this journal is for.
168    */

169   int getTableID() {
170     return table_id;
171   }
172
173   /**
174    * Returns the total number of journal entries.
175    */

176   int entries() {
177     return journal_entries;
178   }
179
180   /**
181    * Returns the command of the nth entry in the journal.
182    */

183   byte getCommand(int n) {
184     return command_journal[n];
185   }
186
187   /**
188    * Returns the row index of the nth entry in the journal.
189    */

190   int getRowIndex(int n) {
191     return command_parameters.intAt(n);
192   }
193
194   /**
195    * Returns a normalized list of all rows that were added in this journal,
196    * but not including those rows also removed. For example, if rows
197    * 1, 2, and 3 were added and 2 was removed, this will return a list of
198    * 1 and 3.
199    */

200   int[] normalizedAddedRows() {
201     IntegerVector list = new IntegerVector();
202     int size = entries();
203     for (int i = 0; i < size; ++i) {
204       byte tc = getCommand(i);
205       if (tc == TABLE_ADD || tc == TABLE_UPDATE_ADD) {
206         int row_index = getRowIndex(i);
207         // If row added, add to list
208
list.addInt(row_index);
209       }
210       else if (tc == TABLE_REMOVE || tc == TABLE_UPDATE_REMOVE) {
211         // If row removed, if the row is already in the list
212
// it's removed from the list, otherwise we leave as is.
213
int row_index = getRowIndex(i);
214         int found_at = list.indexOf(row_index);
215         if (found_at != -1) {
216           list.removeIntAt(found_at);
217         }
218       }
219       else {
220         throw new Error JavaDoc("Unknown command in journal.");
221       }
222     }
223
224     return list.toIntArray();
225   }
226
227   /**
228    * Returns a normalized list of all rows that were removed from this
229    * journal.
230    */

231   int[] normalizedRemovedRows() {
232     IntegerVector list = new IntegerVector();
233     int size = entries();
234     for (int i = 0; i < size; ++i) {
235       byte tc = getCommand(i);
236       if (tc == TABLE_REMOVE || tc == TABLE_UPDATE_REMOVE) {
237         // If removed add to the list.
238
int row_index = getRowIndex(i);
239         list.addInt(row_index);
240       }
241     }
242     return list.toIntArray();
243   }
244
245   /**
246    * Returns three lists - a list of all rows that were inserted, a list of all
247    * rows that were deleted, and a list of all updates. All the lists are
248    * ordered by the order of the command. The update list contains two
249    * entries per 'update', the row that was removed and the row that was
250    * added with the updated info.
251    * <p>
252    * This method is useful for collecting all modification information on the
253    * table.
254    */

255   IntegerVector[] allChangeInformation() {
256     IntegerVector[] lists = new IntegerVector[3];
257     for (int i = 0; i < 3; ++i) {
258       lists[i] = new IntegerVector();
259     }
260     int size = entries();
261     for (int i = 0; i < size; ++i) {
262       byte tc = getCommand(i);
263       int row_index = getRowIndex(i);
264       if (tc == TABLE_ADD) {
265         lists[0].addInt(row_index);
266       }
267       else if (tc == TABLE_REMOVE) {
268         lists[1].addInt(row_index);
269       }
270       else if (tc == TABLE_UPDATE_ADD || tc == TABLE_UPDATE_REMOVE) {
271         lists[2].addInt(row_index);
272       }
273       else {
274         throw new RuntimeException JavaDoc("Don't understand journal command.");
275       }
276     }
277     return lists;
278   }
279
280   /**
281    * Rolls back the last n entries of this journal. This method takes into
282    * account the transient nature of rows (all added rows in the journal are
283    * exclusively referenced by this journal). The algorithm works as follows;
284    * any rows added are deleted, and rows deleted (that weren't added) are
285    * removed from the journal.
286    */

287   void rollbackEntries(int n) {
288     if (n > journal_entries) {
289       throw new RuntimeException JavaDoc(
290           "Trying to roll back more journal entries than are in the journal.");
291     }
292
293     IntegerVector to_add = new IntegerVector();
294     
295     // Find all entries and added new rows to the table
296
int size = entries();
297     for (int i = size - n; i < size; ++i) {
298       byte tc = getCommand(i);
299       if (tc == TABLE_ADD || tc == TABLE_UPDATE_ADD) {
300         to_add.addInt(getRowIndex(i));
301       }
302     }
303
304     // Delete the top entries
305
removeTopEntries(n);
306     // Mark all added entries to deleted.
307
for (int i = 0; i < to_add.size(); ++i) {
308       addEntry(TABLE_ADD, to_add.intAt(i));
309       addEntry(TABLE_REMOVE, to_add.intAt(i));
310     }
311     
312   }
313
314
315
316   // ---------- Testing methods ----------
317

318   /**
319    * Throws a transaction clash exception if it detects a clash between
320    * journal entries. It assumes that this journal is the journal that is
321    * attempting to be compatible with the given journal. A journal clashes
322    * when they both contain a row that is deleted.
323    */

324   void testCommitClash(DataTableDef table_def, MasterTableJournal journal)
325                                                  throws TransactionException {
326     // Very nasty search here...
327
// int cost = entries() * journal.entries();
328
// System.out.print(" CLASH COST = " + cost + " ");
329

330     for (int i = 0; i < entries(); ++i) {
331       byte tc = getCommand(i);
332       if (isRemoveCommand(tc)) { // command - row remove
333
int row_index = getRowIndex(i);
334 // System.out.println("* " + row_index);
335
for (int n = 0; n < journal.entries(); ++n) {
336 // System.out.print(" " + journal.getRowIndex(n));
337
if (isRemoveCommand(journal.getCommand(n)) &&
338               journal.getRowIndex(n) == row_index) {
339             throw new TransactionException(
340                TransactionException.ROW_REMOVE_CLASH,
341                "Concurrent Serializable Transaction Conflict(1): " +
342                "Current row remove clash ( row: " + row_index + ", table: " +
343                table_def.getTableName() + " )");
344           }
345         }
346 // System.out.println();
347
}
348     }
349   }
350
351
352   // ---------- Stream serialization methods ----------
353

354   /**
355    * Reads the journal entries from the given DataInputStream to this object.
356    * <p>
357    * This method is only around because we might need it to convert a
358    * 0.91 era database that stored index data as journals in the file system.
359    */

360   void readFrom(DataInputStream din) throws IOException {
361     commit_id = din.readInt();
362     table_id = din.readInt();
363
364     journal_entries = din.readInt();
365     command_journal = new byte[journal_entries];
366     din.readFully(command_journal, 0, journal_entries);
367     int size = din.readInt();
368     for (int i = 0; i < size; ++i) {
369       command_parameters.addInt(din.readInt());
370     }
371   }
372
373   /**
374    * Debugging.
375    */

376   public String JavaDoc toString() {
377     StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
378     buf.append("[MasterTableJournal] [");
379     buf.append(commit_id);
380     buf.append("] (");
381     for (int i = 0; i < entries(); ++i) {
382       byte c = getCommand(i);
383       int row_index = getRowIndex(i);
384       buf.append("(");
385       buf.append(c);
386       buf.append(")");
387       buf.append(row_index);
388       buf.append(" ");
389     }
390     buf.append(")");
391     return new String JavaDoc(buf);
392   }
393
394 }
395
Popular Tags