1 33 package smallsql.database; 34 35 import java.io.*; 36 import java.sql.*; 37 import java.util.ArrayList ; 38 import java.util.HashMap ; 39 import java.util.Iterator ; 40 import java.util.List ; 41 42 43 class Table extends TableView{ 44 45 private static final int INDEX = 1; 46 47 final Database database; 48 RandomAccessFile raFile; private Lobs lobs; long firstPage; 52 final private HashMap locks = new HashMap (); 53 private SSConnection tabLockConnection; private int tabLockCount; 55 final private ArrayList locksInsert = new ArrayList (); final private HashMap serializeConnections = new HashMap (); 57 final IndexDescriptions indexes; 58 final ForeignKeys references; 59 60 61 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 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 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 115 Table(Database database, String name){ 116 super( name, null); 117 this.database = database; 118 indexes = null; 119 references = null; 120 } 121 122 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 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 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 if(indexes != null) 154 indexes.close(); 155 raFile.close(); 156 raFile = null; 157 } 158 159 160 private void write(SSConnection con) throws Exception { 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 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 ); 177 indexDesc.save(store); 179 180 int offsetEnd = store.getCurrentOffsetInPage(); 182 store.setCurrentOffsetInPage( offsetStart ); 183 store.writeInt( offsetEnd - offsetStart); 184 store.setCurrentOffsetInPage( offsetEnd ); 185 } 186 store.writeInt( 0 ); 188 store.writeFinsh(con); 189 firstPage = store.getNextPagePos(); 190 } 191 192 193 void writeMagic(RandomAccessFile raFile) throws Exception { 194 raFile.writeInt(MAGIC_TABLE); 195 raFile.writeInt(TABLE_VIEW_VERSION); 196 } 197 198 199 202 203 StoreImpl getStore( SSConnection con, long filePos, int pageOperation ) throws Exception { 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 { 210 return StoreImpl.recreateStore( this, storePage, pageOperation ); 212 } 213 214 221 222 223 StoreImpl getStoreInsert( SSConnection con ) throws Exception { 224 TableStorePage storePage = requestLock( con, SQLTokenizer.INSERT, -1 ); 225 return StoreImpl.createStore( this, storePage, SQLTokenizer.INSERT, -1 ); 226 } 227 228 229 232 StoreImpl getStoreTemp( SSConnection con ) throws Exception { 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 { 239 if(lobs == null){ 240 lobs = new Lobs( this ); 241 } 242 return lobs.getStore( con, filePos, pageOperation ); 243 } 244 245 246 247 251 final long getFirstPage(){ 252 return firstPage; 253 } 254 255 256 260 List getInserts(SSConnection con){ 261 synchronized(locks){ 262 ArrayList inserts = new ArrayList (); 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 { 281 synchronized(locks){ 282 long endTime = 0; 283 while(true){ 284 TableStorePage storePage = requestLockImpl( con, pageOperation, page); 285 if(storePage != null) 286 return storePage; 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 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 if(locks.size() > 0){ 317 Iterator 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 TableStorePageInsert lock = (TableStorePageInsert)locksInsert.get(i); 326 if(lock.con != con) return null; 327 } 328 if(serializeConnections.size() > 0){ 329 Iterator 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(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 pageKey = new Long (page); 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; 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 return new TableStorePage( con, this, LOCK_INSERT, -1); 373 default: 374 throw new Error ("pageOperation:"+pageOperation); 375 } 376 } 377 } 378 379 380 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 pageKey = new Long (readlock.fileOffset); TableStorePage lockFirst; 393 TableStorePage lock = lockFirst = (TableStorePage)locks.get( pageKey ); 394 while(lock != null){ 395 if(lock.con != con) return null; if(lock.lockType < LOCK_WRITE){ 397 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 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 if(lock == prev){ 430 if(lock.nextLock == null){ 431 locksInsert.remove(i--); 433 }else{ 434 locksInsert.set( i, lock.nextLock ); 436 } 437 }else{ 438 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 pageKey = new Long (fileOffset); lock = (TableStorePage)locks.get( pageKey ); 452 prev = lock; 453 while(lock != null){ 454 if(lock == storePage){ 455 if(lock == prev){ 457 if(lock.nextLock == null){ 458 locks.remove(pageKey); 460 }else{ 461 locks.put( pageKey, lock.nextLock ); 463 } 464 }else{ 465 prev = lock.nextLock; 467 } 468 return; 469 } 470 prev = lock; 471 lock = lock.nextLock; 472 } 473 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 (); 481 } 482 }finally{ 483 locks.notifyAll(); 484 } 485 } 486 } 487 488 } 489 490 | Popular Tags |