EMMA Coverage Report (generated Wed Jun 28 19:54:35 CEST 2006)
[all classes][smallsql.database]

COVERAGE SUMMARY FOR SOURCE FILE [Table.java]

nameclass, %method, %block, %line, %
Table.java100% (1/1)100% (20/20)83%  (906/1094)87%  (188,1/217)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class Table100% (1/1)100% (20/20)83%  (906/1094)87%  (188,1/217)
drop (SSConnection): void 100% (1/1)62%  (40/64)87%  (10,4/12)
requestLockImpl (SSConnection, int, long): TableStorePage 100% (1/1)66%  (176/266)69%  (31,7/46)
<static initializer> 100% (1/1)68%  (15/22)68%  (0,7/1)
freeLock (TableStorePage): void 100% (1/1)73%  (123/169)81%  (31,5/39)
requestWriteLock (SSConnection, TableStorePage): TableStorePage 100% (1/1)77%  (60/78)72%  (13/18)
getInserts (SSConnection): List 100% (1/1)95%  (62/65)98%  (10,8/11)
Table (Database, SSConnection, String, Columns, IndexDescriptions, ForeignKey... 100% (1/1)100% (59/59)100% (14/14)
Table (Database, String): void 100% (1/1)100% (29/29)100% (8/8)
Table (SSConnection, String, RandomAccessFile, long, int): void 100% (1/1)100% (98/98)100% (21/21)
close (): void 100% (1/1)100% (7/7)100% (3/3)
drop (Database, String): void 100% (1/1)100% (22/22)100% (3/3)
getFirstPage (): long 100% (1/1)100% (3/3)100% (1/1)
getLobStore (SSConnection, long, int): StoreImpl 100% (1/1)100% (16/16)100% (3/3)
getStore (SSConnection, long, int): StoreImpl 100% (1/1)100% (12/12)100% (2/2)
getStore (TableStorePage, int): StoreImpl 100% (1/1)100% (5/5)100% (1/1)
getStoreInsert (SSConnection): StoreImpl 100% (1/1)100% (12/12)100% (2/2)
getStoreTemp (SSConnection): StoreImpl 100% (1/1)100% (14/14)100% (2/2)
requestLock (SSConnection, int, long): TableStorePage 100% (1/1)100% (55/55)100% (12/12)
write (SSConnection): void 100% (1/1)100% (91/91)100% (21/21)
writeMagic (RandomAccessFile): void 100% (1/1)100% (7/7)100% (3/3)

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 */
33package smallsql.database;
34 
35import java.io.*;
36import java.sql.*;
37import java.util.ArrayList;
38import java.util.HashMap;
39import java.util.Iterator;
40import java.util.List;
41 
42 
43class 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 locks = new HashMap();
53        private SSConnection tabLockConnection; // wenn gesetzt die Connection die ein LOCK_TAB hat
54        private int tabLockCount;
55        final private ArrayList locksInsert = new ArrayList(); // liste der LOCK_INSERT
56        final private HashMap serializeConnections = new HashMap();
57        final IndexDescriptions indexes;
58        final ForeignKeys references;
59 
60 
61        /**
62         * Constructor for read existing tables.
63         */
64    Table(SSConnection con, String name, RandomAccessFile raFile, long offset, int tableFormatVersion) throws Exception{
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 name, Columns columns, IndexDescriptions indexes, ForeignKeys foreignKeys) throws Exception{
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 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 name) throws Exception{
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{
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{
153        raFile.close();
154        raFile = null;
155    }
156 
157 
158    private void write(SSConnection con) throws Exception{
159        raFile = createFile( database );
160        firstPage = 8;
161        StoreImpl store = getStore( con, firstPage, SQLTokenizer.CREATE);
162        int count = columns.size();
163        store.writeInt( count );
164        for(int i=0; i<count; i++){
165            store.writeColumn( this, columns.get(i) );
166        }
167 
168                // write additional informations
169                for(int i=0; i<indexes.size(); i++){
170                        IndexDescription indexDesc = indexes.get(i);
171                        store.writeInt( INDEX );
172                        int offsetStart = store.getCurrentOffsetInPage();
173                        store.setCurrentOffsetInPage( offsetStart + 4 ); // place holder for length
174                        
175                        // write the IndexDescription
176                        indexDesc.save(store);
177                        
178                        // write the length information
179                        int offsetEnd = store.getCurrentOffsetInPage();
180                        store.setCurrentOffsetInPage( offsetStart );
181                        store.writeInt( offsetEnd - offsetStart);
182                        store.setCurrentOffsetInPage( offsetEnd );
183                }
184                store.writeInt( 0 ); // no more additinal informations
185                
186                store.writeFinsh(con);
187        firstPage = store.getNextPagePos();
188    }
189    
190 
191        void writeMagic(RandomAccessFile raFile) throws Exception{
192                raFile.writeInt(MAGIC_TABLE);
193                raFile.writeInt(TABLE_VIEW_VERSION);
194        }
195        
196 
197    /*StoreImpl getStoreCreate( SSConnection con, long filePos ) throws Exception{
198        return StoreImpl.createStore( con, raFile, SQLTokenizer.CREATE, filePos );
199    }*/
200 
201    StoreImpl getStore( SSConnection con, long filePos, int pageOperation ) throws Exception{
202                TableStorePage storePage = requestLock( con, pageOperation, filePos );
203        return StoreImpl.createStore( this, storePage, pageOperation, filePos );
204    }
205 
206    
207        StoreImpl getStore( TableStorePage storePage, int pageOperation ) throws Exception{
208                // is used for not commited INSERT pages, a new lock is not needed
209                return StoreImpl.recreateStore( this, storePage, pageOperation );
210        }
211        
212    /*StoreImpl getStoreUpdate( SSConnection con, long filePos ) throws Exception{
213        return StoreImpl.createStore( con, raFile, SQLTokenizer.UPDATE, filePos );
214    }
215 
216    StoreImpl getStoreDelete( SSConnection con, long filePos ) throws Exception{
217        return StoreImpl.createStore( con, raFile, SQLTokenizer.DELETE, filePos );
218    }*/
219        
220 
221    StoreImpl getStoreInsert( SSConnection con ) throws Exception{
222                TableStorePage storePage = requestLock( con, SQLTokenizer.INSERT, -1 );
223        return StoreImpl.createStore( this, storePage, SQLTokenizer.INSERT, -1 );
224    }
225    
226    
227    /**
228     * Create a Store that is not invoke in a transaction for copy of data.
229     */
230        StoreImpl getStoreTemp( SSConnection con ) throws Exception{
231                TableStorePage storePage = new TableStorePage( con, this, LOCK_NONE, -2);
232                return StoreImpl.createStore( this, storePage, SQLTokenizer.INSERT, -2 );
233        }
234        
235 
236        StoreImpl getLobStore(SSConnection con, long filePos, int pageOperation) throws Exception{
237                if(lobs == null){
238                        lobs = new Lobs( this );
239                }
240                return lobs.getStore( con, filePos, pageOperation );
241        }
242    
243 
244        
245        /**
246         * Return the file offset of the first page with data after the table declaration.
247         * This is equals to the first row.
248         */
249    final long getFirstPage(){
250        return firstPage;
251    }
252 
253 
254    /**
255     * Return a list of Links to not commited rows. The list include only the rows that are visible for 
256     * the current isolation level.
257     */
258    List getInserts(SSConnection con){
259                synchronized(locks){
260                        ArrayList inserts = new ArrayList();
261                        if(con.isolationLevel <= Connection.TRANSACTION_READ_UNCOMMITTED){
262                                for(int i=0; i<locksInsert.size(); i++){
263                                        TableStorePageInsert lock = (TableStorePageInsert)locksInsert.get(i);
264                                        inserts.add(lock.getLink());
265                                }
266                        }else{
267                                for(int i=0; i<locksInsert.size(); i++){
268                                        TableStorePageInsert lock = (TableStorePageInsert)locksInsert.get(i);
269                                        if(lock.con == con)
270                                                inserts.add(lock.getLink());
271                                }
272                        }
273                        return inserts;
274                }            
275    }
276    
277    
278    final private TableStorePage requestLock(SSConnection con, int pageOperation, long page) throws Exception{
279            synchronized(locks){
280                        long endTime = 0;
281                        while(true){
282                                TableStorePage storePage = requestLockImpl( con, pageOperation, page);
283                                if(storePage != null) 
284                                        return storePage; // the normal case shoud be the fasted
285                                if(endTime == 0)
286                                        endTime = System.currentTimeMillis() + 5000;
287                                long waitTime = endTime - System.currentTimeMillis();
288                                if(waitTime <= 0)
289                                        throw Utils.createSQLException("Deadlock, can not create a lock on table '"+name+"'");
290                                locks.wait(waitTime);
291                        }
292            }
293    }
294    
295    /**
296     * Request an page lock. If the rquest is valid then it return the StorePage. 
297     * In the other case it return null.
298     * @param page The fileOffset or -1 for a new page
299     * @throws SQLException 
300     */
301        final private TableStorePage requestLockImpl(SSConnection con, int pageOperation, long page) throws SQLException{
302                synchronized(locks){
303                        if(tabLockConnection != null && tabLockConnection != con) return null;
304                        switch(con.isolationLevel){
305                                case Connection.TRANSACTION_SERIALIZABLE:
306                                        con.serializeCount++;
307                                        serializeConnections.put( con, con);
308                                        break;
309                        }
310                
311                        switch(pageOperation){
312                                case SQLTokenizer.CREATE:{
313                                                // first check if another connection has a lock bevore creating a table lock
314                                                if(locks.size() > 0){
315                                                        Iterator values = locks.values().iterator();
316                                                        while(values.hasNext()){
317                                                                TableStorePage lock = (TableStorePage)values.next();
318                                                                if(lock.con != con) return null;
319                                                        }
320                                                }
321                                                for(int i=0; i<locksInsert.size(); i++){
322                                                        //the first StorePage in the linked list must be ever TableStorePageInsert
323                                                        TableStorePageInsert lock = (TableStorePageInsert)locksInsert.get(i);
324                                                        if(lock.con != con) return null;
325                                                }
326                                                if(serializeConnections.size() > 0){
327                                                        Iterator values = serializeConnections.values().iterator();
328                                                        while(values.hasNext()){
329                                                                TableStorePage lock = (TableStorePage)values.next();
330                                                                if(lock.con != con) return null;
331                                                        }
332                                                }
333                                                tabLockConnection = con;
334                                                tabLockCount++;
335                                                TableStorePage lock = new TableStorePage(con, this, LOCK_TAB, page);
336                                                con.add(lock);
337                                                return lock;
338                                        }
339                                case SQLTokenizer.INSERT:{
340                                                // if there are more as one Connection with a serializable lock then an INSERT is not valid
341                                                if(serializeConnections.size() > 1) return null;
342                                                if(serializeConnections.size() == 1 && serializeConnections.get(con) != null) return null;
343                                                TableStorePageInsert lock = new TableStorePageInsert(con, this, LOCK_INSERT);
344                                                locksInsert.add( lock );
345                                                con.add(lock);
346                                                return lock;
347                                        }
348                                case SQLTokenizer.SELECT:{
349                                                Long pageKey = new Long(page); //TODO performance
350                                                TableStorePage lockFirst;
351                                                TableStorePage lock = lockFirst = (TableStorePage)locks.get( pageKey );
352                                                while(lock != null){
353                                                        if(lock.con == con || 
354                                                           con.isolationLevel <= Connection.TRANSACTION_READ_UNCOMMITTED) return lock;
355                                                        if(lock.lockType == LOCK_WRITE) return null; // write lock of another Connection
356                                                        lock = lock.nextLock;
357                                                }
358                                                lock = new TableStorePage( con, this, LOCK_NONE, page);
359                                                if(con.isolationLevel >= Connection.TRANSACTION_REPEATABLE_READ){
360                                                        lock.lockType = LOCK_READ;
361                                                        lock.nextLock = lockFirst;
362                                                        locks.put( pageKey, lock );
363                                                        con.add(lock);
364                                                }
365                                                return lock;                                                        
366                                        }
367                                case SQLTokenizer.LONGVARBINARY:
368                                        // is used for written BLOB and CLOB
369                                        // the difference to INSERT is that page descript the size of the byte buffer
370                                        return new TableStorePage( con, this, LOCK_INSERT, -1);
371                                default:
372                                        throw new Error("pageOperation:"+pageOperation);
373                        }
374                }
375        }
376        
377        
378        /**
379         * Request a write lock for a page that is read.
380         * @throws SQLException 
381         */
382        TableStorePage requestWriteLock(SSConnection con, TableStorePage readlock) throws SQLException{
383                if(readlock.lockType == LOCK_INSERT){
384                        TableStorePage lock = new TableStorePage( con, this, LOCK_INSERT, -1);
385                        readlock.nextLock = lock;
386                        con.add(lock);
387                        return lock;                                                                        
388                }
389                Long pageKey = new Long(readlock.fileOffset); //TODO performance
390                TableStorePage lockFirst;
391                TableStorePage lock = lockFirst = (TableStorePage)locks.get( pageKey );
392                while(lock != null){
393                        if(lock.con != con) return null; // there is allready any lock from another connection, we can not start write
394                        if(lock.lockType < LOCK_WRITE){
395                                // if there is only a read lock we can transfer it
396                                // this is requied for rollback to a savepoint
397                                lock.lockType = LOCK_WRITE;
398                                return lock;
399                        }
400                        lock = lock.nextLock;
401                }
402                lock = new TableStorePage( con, this, LOCK_WRITE, readlock.fileOffset);
403                lock.nextLock = lockFirst;
404                locks.put( pageKey, lock );
405                con.add(lock);
406                return lock;                                                                        
407        }
408        
409        
410        /**
411         * Remove the lock from this table.
412         */
413        void freeLock(TableStorePage storePage){
414                final int lockType = storePage.lockType;
415                final long fileOffset = storePage.fileOffset;
416                synchronized(locks){
417                        try{
418                                TableStorePage lock;
419                                TableStorePage prev;
420                                switch(lockType){
421                                        case LOCK_INSERT:
422                                                for(int i=0; i<locksInsert.size(); i++){
423                                                        prev = lock = (TableStorePage)locksInsert.get(i);
424                                                        while(lock != null){
425                                                                if(lock == storePage){
426                                                                        //remove lock
427                                                                        if(lock == prev){
428                                                                                if(lock.nextLock == null){
429                                                                                        // the first lock is the only lock in the list
430                                                                                        locksInsert.remove(i--);
431                                                                                }else{
432                                                                                        // only the first lock of the list is remove
433                                                                                        locksInsert.set( i, lock.nextLock );
434                                                                                }
435                                                                        }else{
436                                                                                // a lock in the mid or end is removed
437                                                                                prev.nextLock = lock.nextLock;
438                                                                        }
439                                                                        return;
440                                                                }
441                                                                prev = lock;
442                                                                lock = lock.nextLock;
443                                                        }
444                                                }
445                                                break;
446                                        case LOCK_READ:
447                                        case LOCK_WRITE:
448                                                Long pageKey = new Long(fileOffset); //TODO performance
449                                                lock = (TableStorePage)locks.get( pageKey );
450                                                prev = lock;
451                                                while(lock != null){
452                                                        if(lock == storePage){
453                                                                //lock entfernen
454                                                                if(lock == prev){
455                                                                        if(lock.nextLock == null){
456                                                                                // erste und einzige Lock in Liste
457                                                                                locks.remove(pageKey);
458                                                                        }else{
459                                                                                // erste Lock in liste fällt weg
460                                                                                locks.put( pageKey, lock.nextLock );
461                                                                        }
462                                                                }else{
463                                                                        // lock in mitte oder ende der Liste fällt weg
464                                                                        prev = lock.nextLock;
465                                                                }
466                                                                return;
467                                                        }
468                                                        prev = lock;
469                                                        lock = lock.nextLock;
470                                                }
471                                                // Durchläufer kann auftreten, wenn eine Lock hochgestuft wurde und damit der type nicht stimmt
472                                                break;
473                                        case LOCK_TAB:
474                                                assert storePage.con == tabLockConnection : "Internale Error with TabLock";
475                                                if(--tabLockCount == 0) tabLockConnection = null;
476                                                break;
477                                        default:
478                                                throw new Error();
479                                }
480                        }finally{
481                                locks.notifyAll();
482                        }
483                }
484        }
485 
486}
487 

[all classes][smallsql.database]
EMMA 2.1.5320 (stable) (C) Vladimir Roubtsov