KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > smallsql > database > Table


1 /* =============================================================
2  * SmallSQL : a free Java DBMS library for the Java(tm) platform
3  * =============================================================
4  *
5  * (C) Copyright 2004-2006, by Volker Berlin.
6  *
7  * Project Info: http://www.smallsql.de/
8  *
9  * This library is free software; you can redistribute it and/or modify it
10  * under the terms of the GNU Lesser General Public License as published by
11  * the Free Software Foundation; either version 2.1 of the License, or
12  * (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful, but
15  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
16  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
17  * License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22  * USA.
23  *
24  * [Java is a trademark or registered trademark of Sun Microsystems, Inc.
25  * in the United States and other countries.]
26  *
27  * ---------------
28  * Table.java
29  * ---------------
30  * Author: Volker Berlin
31  *
32  */

33 package smallsql.database;
34
35 import java.io.*;
36 import java.sql.*;
37 import java.util.ArrayList JavaDoc;
38 import java.util.HashMap JavaDoc;
39 import java.util.Iterator JavaDoc;
40 import java.util.List JavaDoc;
41
42
43 class Table extends TableView{
44     
45     private static final int INDEX = 1;
46
47     final Database database;
48     RandomAccessFile raFile; // file handle of the table
49
private Lobs lobs; // file handle of lob data for this table
50
long firstPage; // offset of the first page
51

52     final private HashMap JavaDoc locks = new HashMap JavaDoc();
53     private SSConnection tabLockConnection; // wenn gesetzt die Connection die ein LOCK_TAB hat
54
private int tabLockCount;
55     final private ArrayList JavaDoc locksInsert = new ArrayList JavaDoc(); // liste der LOCK_INSERT
56
final private HashMap JavaDoc serializeConnections = new HashMap JavaDoc();
57     final IndexDescriptions indexes;
58     final ForeignKeys references;
59
60
61     /**
62      * Constructor for read existing tables.
63      */

64     Table(SSConnection con, String JavaDoc name, RandomAccessFile raFile, long offset, int tableFormatVersion) throws Exception JavaDoc{
65         super( name, new Columns() );
66         this.database = con.getDatabase(false);
67         this.raFile = raFile;
68         this.firstPage = offset;
69         StoreImpl store = getStore(con, firstPage, SQLTokenizer.SELECT);
70         int count = store.readInt();
71
72         for(int i=0; i<count; i++){
73             columns.add( store.readColumn(this, tableFormatVersion) );
74         }
75         indexes = new IndexDescriptions();
76         references = new ForeignKeys();
77         
78         // read additional informations
79
int type;
80         while((type = store.readInt()) != 0){
81             int offsetInPage = store.getCurrentOffsetInPage();
82             int size = store.readInt();
83             switch(type){
84                 case INDEX:
85                     indexes.add( IndexDescription.load( database, this, store) );
86                     break;
87             }
88             store.setCurrentOffsetInPage(offsetInPage + size);
89         }
90         
91         firstPage = store.getNextPagePos();
92     }
93     
94
95     /**
96      * Constructor for creating of new tables.
97      */

98     Table(Database database, SSConnection con, String JavaDoc name, Columns columns, IndexDescriptions indexes, ForeignKeys foreignKeys) throws Exception JavaDoc{
99         super( name, columns );
100         this.database = database;
101         this.indexes = indexes;
102         this.references = foreignKeys;
103         indexes.create( database, this );
104         write(con);
105         for(int i=0; i<foreignKeys.size(); i++){
106             ForeignKey foreignKey = foreignKeys.get(i);
107             Table pkTable = (Table)database.getTableView(con, foreignKey.pkTable);
108             pkTable.references.add(foreignKey);
109         }
110     }
111     
112     /**
113      * Constructor for extends class Lobs.
114      */

115     Table(Database database, String JavaDoc name){
116         super( name, null);
117         this.database = database;
118         indexes = null;
119         references = null;
120     }
121
122     /**
123      * Drop the Table. This method is static that the file does not need to load and also corrupt files can be dropped.
124      */

125     static void drop(Database database, String JavaDoc name) throws Exception JavaDoc{
126         boolean ok = new File( Utils.createTableViewFileName( database, name ) ).delete();
127         if(!ok) throw Utils.createSQLException("Table '" + name + "' can't drop.");
128     }
129     
130     
131     /**
132      * Drop a loaded table.
133      *
134      */

135     void drop(SSConnection con) throws Exception JavaDoc{
136         TableStorePage storePage = requestLock( con, SQLTokenizer.CREATE, -1 );
137         if(storePage == null)
138             throw Utils.createSQLException("Table '" + name + "' can't drop because is locked.");
139         
140         // remove the all commits that point to this table
141
con.rollbackFile(raFile);
142         close();
143         if(lobs != null)
144             lobs.drop(con);
145         if(indexes != null)
146             indexes.drop(database);
147         boolean ok = getFile( database, name).delete();
148         if(!ok) throw Utils.createSQLException("Table '" + name + "' can't drop.");
149     }
150     
151
152     void close() throws Exception JavaDoc{
153         if(indexes != null)
154             indexes.close();
155         raFile.close();
156         raFile = null;
157     }
158
159
160     private void write(SSConnection con) throws Exception JavaDoc{
161         raFile = createFile( database );
162         firstPage = 8;
163         StoreImpl store = getStore( con, firstPage, SQLTokenizer.CREATE);
164         int count = columns.size();
165         store.writeInt( count );
166         for(int i=0; i<count; i++){
167             store.writeColumn( this, columns.get(i) );
168         }
169
170         // write additional informations
171
for(int i=0; i<indexes.size(); i++){
172             IndexDescription indexDesc = indexes.get(i);
173             store.writeInt( INDEX );
174             int offsetStart = store.getCurrentOffsetInPage();
175             store.setCurrentOffsetInPage( offsetStart + 4 ); // place holder for length
176

177             // write the IndexDescription
178
indexDesc.save(store);
179             
180             // write the length information
181
int offsetEnd = store.getCurrentOffsetInPage();
182             store.setCurrentOffsetInPage( offsetStart );
183             store.writeInt( offsetEnd - offsetStart);
184             store.setCurrentOffsetInPage( offsetEnd );
185         }
186         store.writeInt( 0 ); // no more additinal informations
187

188         store.writeFinsh(con);
189         firstPage = store.getNextPagePos();
190     }
191     
192
193     void writeMagic(RandomAccessFile raFile) throws Exception JavaDoc{
194         raFile.writeInt(MAGIC_TABLE);
195         raFile.writeInt(TABLE_VIEW_VERSION);
196     }
197     
198
199     /*StoreImpl getStoreCreate( SSConnection con, long filePos ) throws Exception{
200         return StoreImpl.createStore( con, raFile, SQLTokenizer.CREATE, filePos );
201     }*/

202
203     StoreImpl getStore( SSConnection con, long filePos, int pageOperation ) throws Exception JavaDoc{
204         TableStorePage storePage = requestLock( con, pageOperation, filePos );
205         return StoreImpl.createStore( this, storePage, pageOperation, filePos );
206     }
207
208     
209     StoreImpl getStore( TableStorePage storePage, int pageOperation ) throws Exception JavaDoc{
210         // is used for not commited INSERT pages, a new lock is not needed
211
return StoreImpl.recreateStore( this, storePage, pageOperation );
212     }
213     
214     /*StoreImpl getStoreUpdate( SSConnection con, long filePos ) throws Exception{
215         return StoreImpl.createStore( con, raFile, SQLTokenizer.UPDATE, filePos );
216     }
217
218     StoreImpl getStoreDelete( SSConnection con, long filePos ) throws Exception{
219         return StoreImpl.createStore( con, raFile, SQLTokenizer.DELETE, filePos );
220     }*/

221     
222
223     StoreImpl getStoreInsert( SSConnection con ) throws Exception JavaDoc{
224         TableStorePage storePage = requestLock( con, SQLTokenizer.INSERT, -1 );
225         return StoreImpl.createStore( this, storePage, SQLTokenizer.INSERT, -1 );
226     }
227     
228     
229     /**
230      * Create a Store that is not invoke in a transaction for copy of data.
231      */

232     StoreImpl getStoreTemp( SSConnection con ) throws Exception JavaDoc{
233         TableStorePage storePage = new TableStorePage( con, this, LOCK_NONE, -2);
234         return StoreImpl.createStore( this, storePage, SQLTokenizer.INSERT, -2 );
235     }
236         
237
238     StoreImpl getLobStore(SSConnection con, long filePos, int pageOperation) throws Exception JavaDoc{
239         if(lobs == null){
240             lobs = new Lobs( this );
241         }
242         return lobs.getStore( con, filePos, pageOperation );
243     }
244     
245
246     
247     /**
248      * Return the file offset of the first page with data after the table declaration.
249      * This is equals to the first row.
250      */

251     final long getFirstPage(){
252         return firstPage;
253     }
254
255
256     /**
257      * Return a list of Links to not commited rows. The list include only the rows that are visible for
258      * the current isolation level.
259      */

260     List JavaDoc getInserts(SSConnection con){
261         synchronized(locks){
262             ArrayList JavaDoc inserts = new ArrayList JavaDoc();
263             if(con.isolationLevel <= Connection.TRANSACTION_READ_UNCOMMITTED){
264                 for(int i=0; i<locksInsert.size(); i++){
265                     TableStorePageInsert lock = (TableStorePageInsert)locksInsert.get(i);
266                     inserts.add(lock.getLink());
267                 }
268             }else{
269                 for(int i=0; i<locksInsert.size(); i++){
270                     TableStorePageInsert lock = (TableStorePageInsert)locksInsert.get(i);
271                     if(lock.con == con)
272                         inserts.add(lock.getLink());
273                 }
274             }
275             return inserts;
276         }
277     }
278     
279     
280     final private TableStorePage requestLock(SSConnection con, int pageOperation, long page) throws Exception JavaDoc{
281         synchronized(locks){
282             long endTime = 0;
283             while(true){
284                 TableStorePage storePage = requestLockImpl( con, pageOperation, page);
285                 if(storePage != null)
286                     return storePage; // the normal case shoud be the fasted
287
if(endTime == 0)
288                     endTime = System.currentTimeMillis() + 5000;
289                 long waitTime = endTime - System.currentTimeMillis();
290                 if(waitTime <= 0)
291                     throw Utils.createSQLException("Deadlock, can not create a lock on table '"+name+"'");
292                 locks.wait(waitTime);
293             }
294         }
295     }
296     
297     /**
298      * Request an page lock. If the rquest is valid then it return the StorePage.
299      * In the other case it return null.
300      * @param page The fileOffset or -1 for a new page
301      * @throws SQLException
302      */

303     final private TableStorePage requestLockImpl(SSConnection con, int pageOperation, long page) throws SQLException{
304         synchronized(locks){
305             if(tabLockConnection != null && tabLockConnection != con) return null;
306             switch(con.isolationLevel){
307                 case Connection.TRANSACTION_SERIALIZABLE:
308                     con.serializeCount++;
309                     serializeConnections.put( con, con);
310                     break;
311             }
312         
313             switch(pageOperation){
314                 case SQLTokenizer.CREATE:{
315                         // first check if another connection has a lock bevore creating a table lock
316
if(locks.size() > 0){
317                             Iterator JavaDoc values = locks.values().iterator();
318                             while(values.hasNext()){
319                                 TableStorePage lock = (TableStorePage)values.next();
320                                 if(lock.con != con) return null;
321                             }
322                         }
323                         for(int i=0; i<locksInsert.size(); i++){
324                             //the first StorePage in the linked list must be ever TableStorePageInsert
325
TableStorePageInsert lock = (TableStorePageInsert)locksInsert.get(i);
326                             if(lock.con != con) return null;
327                         }
328                         if(serializeConnections.size() > 0){
329                             Iterator JavaDoc values = serializeConnections.values().iterator();
330                             while(values.hasNext()){
331                                 TableStorePage lock = (TableStorePage)values.next();
332                                 if(lock.con != con) return null;
333                             }
334                         }
335                         tabLockConnection = con;
336                         tabLockCount++;
337                         TableStorePage lock = new TableStorePage(con, this, LOCK_TAB, page);
338                         con.add(lock);
339                         return lock;
340                     }
341                 case SQLTokenizer.INSERT:{
342                         // if there are more as one Connection with a serializable lock then an INSERT is not valid
343
if(serializeConnections.size() > 1) return null;
344                         if(serializeConnections.size() == 1 && serializeConnections.get(con) != null) return null;
345                         TableStorePageInsert lock = new TableStorePageInsert(con, this, LOCK_INSERT);
346                         locksInsert.add( lock );
347                         con.add(lock);
348                         return lock;
349                     }
350                 case SQLTokenizer.SELECT:{
351                         Long JavaDoc pageKey = new Long JavaDoc(page); //TODO performance
352
TableStorePage lockFirst;
353                         TableStorePage lock = lockFirst = (TableStorePage)locks.get( pageKey );
354                         while(lock != null){
355                             if(lock.con == con ||
356                                con.isolationLevel <= Connection.TRANSACTION_READ_UNCOMMITTED) return lock;
357                             if(lock.lockType == LOCK_WRITE) return null; // write lock of another Connection
358
lock = lock.nextLock;
359                         }
360                         lock = new TableStorePage( con, this, LOCK_NONE, page);
361                         if(con.isolationLevel >= Connection.TRANSACTION_REPEATABLE_READ){
362                             lock.lockType = LOCK_READ;
363                             lock.nextLock = lockFirst;
364                             locks.put( pageKey, lock );
365                             con.add(lock);
366                         }
367                         return lock;
368                     }
369                 case SQLTokenizer.LONGVARBINARY:
370                     // is used for written BLOB and CLOB
371
// the difference to INSERT is that page descript the size of the byte buffer
372
return new TableStorePage( con, this, LOCK_INSERT, -1);
373                 default:
374                     throw new Error JavaDoc("pageOperation:"+pageOperation);
375             }
376         }
377     }
378     
379     
380     /**
381      * Request a write lock for a page that is read.
382      * @throws SQLException
383      */

384     TableStorePage requestWriteLock(SSConnection con, TableStorePage readlock) throws SQLException{
385         if(readlock.lockType == LOCK_INSERT){
386             TableStorePage lock = new TableStorePage( con, this, LOCK_INSERT, -1);
387             readlock.nextLock = lock;
388             con.add(lock);
389             return lock;
390         }
391         Long JavaDoc pageKey = new Long JavaDoc(readlock.fileOffset); //TODO performance
392
TableStorePage lockFirst;
393         TableStorePage lock = lockFirst = (TableStorePage)locks.get( pageKey );
394         while(lock != null){
395             if(lock.con != con) return null; // there is already any lock from another connection, we can not start write
396
if(lock.lockType < LOCK_WRITE){
397                 // if there is only a read lock we can transfer it
398
// this is requied for rollback to a savepoint
399
lock.lockType = LOCK_WRITE;
400                 return lock;
401             }
402             lock = lock.nextLock;
403         }
404         lock = new TableStorePage( con, this, LOCK_WRITE, readlock.fileOffset);
405         lock.nextLock = lockFirst;
406         locks.put( pageKey, lock );
407         con.add(lock);
408         return lock;
409     }
410     
411     
412     /**
413      * Remove the lock from this table.
414      */

415     void freeLock(TableStorePage storePage){
416         final int lockType = storePage.lockType;
417         final long fileOffset = storePage.fileOffset;
418         synchronized(locks){
419             try{
420                 TableStorePage lock;
421                 TableStorePage prev;
422                 switch(lockType){
423                     case LOCK_INSERT:
424                         for(int i=0; i<locksInsert.size(); i++){
425                             prev = lock = (TableStorePage)locksInsert.get(i);
426                             while(lock != null){
427                                 if(lock == storePage){
428                                     //remove lock
429
if(lock == prev){
430                                         if(lock.nextLock == null){
431                                             // the first lock is the only lock in the list
432
locksInsert.remove(i--);
433                                         }else{
434                                             // only the first lock of the list is remove
435
locksInsert.set( i, lock.nextLock );
436                                         }
437                                     }else{
438                                         // a lock in the mid or end is removed
439
prev.nextLock = lock.nextLock;
440                                     }
441                                     return;
442                                 }
443                                 prev = lock;
444                                 lock = lock.nextLock;
445                             }
446                         }
447                         break;
448                     case LOCK_READ:
449                     case LOCK_WRITE:
450                         Long JavaDoc pageKey = new Long JavaDoc(fileOffset); //TODO performance
451
lock = (TableStorePage)locks.get( pageKey );
452                         prev = lock;
453                         while(lock != null){
454                             if(lock == storePage){
455                                 //lock entfernen
456
if(lock == prev){
457                                     if(lock.nextLock == null){
458                                         // erste und einzige Lock in Liste
459
locks.remove(pageKey);
460                                     }else{
461                                         // erste Lock in liste fällt weg
462
locks.put( pageKey, lock.nextLock );
463                                     }
464                                 }else{
465                                     // lock in mitte oder ende der Liste fällt weg
466
prev = lock.nextLock;
467                                 }
468                                 return;
469                             }
470                             prev = lock;
471                             lock = lock.nextLock;
472                         }
473                         // Durchläufer kann auftreten, wenn eine Lock hochgestuft wurde und damit der type nicht stimmt
474
break;
475                     case LOCK_TAB:
476                         assert storePage.con == tabLockConnection : "Internale Error with TabLock";
477                         if(--tabLockCount == 0) tabLockConnection = null;
478                         break;
479                     default:
480                         throw new Error JavaDoc();
481                 }
482             }finally{
483                 locks.notifyAll();
484             }
485         }
486     }
487
488 }
489
490
Popular Tags