📄 storeimpl.java
字号:
/* =============================================================
* SmallSQL : a free Java DBMS library for the Java(tm) platform
* =============================================================
*
* (C) Copyright 2004-2007, by Volker Berlin.
*
* Project Info: http://www.smallsql.de/
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* [Java is a trademark or registered trademark of Sun Microsystems, Inc.
* in the United States and other countries.]
*
* ---------------
* StoreImpl.java
* ---------------
* Author: Volker Berlin
*
*/
package smallsql.database;
import java.io.*;
import java.sql.SQLException;
import smallsql.database.language.Language;
public class StoreImpl extends Store {
private static final int DEFAULT_PAGE_SIZE = 8192; // 8 Kb
private static final int PAGE_MAGIC = 0x12DD13DE; // are used for repairing a table
/**
* The structure of the Page Control Block is:
* 4 byte - page magic
* 4 Byte - Status 0:normal; 1:deleted; 2:Pointer to an update; 3: updated page
* 4 Byte - used size of the page
* 4 byte - physical size of the page
* 4 byte - offset to the next page
* 8 byte - position of an updated page
*/
private static final int PAGE_CONTROL_SIZE = 28;
private static final byte[] page_control = new byte[PAGE_CONTROL_SIZE];
private int status; // valid value are follow:
private static final int NORMAL = 0;
private static final int DELETED = 1;
/**
* Using of UPDATE_POINTER and UPDATED_PAGE
* If a page are updated and the new data are larger as the old data then
* the old page are changed to a UPDATE_POINTER. The new page is
* a UPDATED_PAGE. On reading the pages only the UPDATE_POINTER is read.
* The UPDATED_PAGE are skipped. Thats a row change not it position.
*/
private static final int UPDATE_POINTER = 2;
private static final int UPDATED_PAGE = 3;
final private Table table;
private byte[] page; // Data of one page
private StorePage storePage;
private long filePos; // Position in the file
private int sizeUsed;
private int sizePhysical;
private int nextPageOffset;
private long filePosUpdated;
private int type;
private StoreImpl updatePointer;
private StoreImpl( Table table, StorePage storePage, int type, long filePos ){
this.table = table;
this.storePage = storePage;
this.filePos = filePos;
this.type = type;
}
/**
* Follow types of StoreImpl are possible:
* INSERT: A page that will be include new data. filePos is not define yet.
* CREATE: A special type of INSERT
* SELECT: Only read operations are possible.
* UPDATE: Has a filePos, if the new size is to small then the old page must be deleted and a new added.
* DELETE: Has no cache else only the filePos to write the flag.
*/
static StoreImpl createStore( Table table, StorePage storePage, int type, long filePos ) throws SQLException{
try {
StoreImpl store = new StoreImpl(table, storePage, type, filePos);
RandomAccessFile raFile = storePage.raFile;
switch(type){
case SQLTokenizer.LONGVARBINARY:
// wird verwendet zum speichern von LONGVARBINARY und LONGVARCHAR
store.page = new byte[(int)filePos + PAGE_CONTROL_SIZE];
store.filePos = -1;
break;
case SQLTokenizer.INSERT:
case SQLTokenizer.CREATE:
store.page = new byte[DEFAULT_PAGE_SIZE];
break;
case SQLTokenizer.SELECT:
case SQLTokenizer.UPDATE:
case SQLTokenizer.DELETE:
if(storePage.page == null){
if(filePos >= raFile.length()-PAGE_CONTROL_SIZE){
return null;
}
raFile.seek(filePos);
synchronized(page_control){
raFile.read(page_control);
store.page = page_control;
store.readPageHeader();
}
store.page = new byte[store.sizeUsed];
raFile.seek(filePos);
raFile.read(store.page);
}else{
store.page = storePage.page;
store.readPageHeader();
}
store = store.loadUpdatedStore();
break;
default: throw new Error();
}
store.offset = PAGE_CONTROL_SIZE;
return store;
} catch (Throwable th) {
throw SmallSQLException.createFromException(th);
}
}
/**
* Recreate a StoreImpl from an uncommitted StorePage.
*/
static StoreImpl recreateStore( Table table, StorePage storePage, int type) throws Exception{
StoreImpl store = new StoreImpl(table, storePage, type, -1);
store.page = storePage.page;
store.readPageHeader();
store = store.loadUpdatedStore();
store.offset = PAGE_CONTROL_SIZE;
return store;
}
private final void readPageHeader() throws SQLException{
if(readInt() != PAGE_MAGIC)
throw SmallSQLException.create(Language.TABLE_CORRUPT_PAGE, new Object[] { new Long(filePos) });
status = readInt();
sizeUsed = readInt();
sizePhysical = readInt();
nextPageOffset = readInt();
filePosUpdated = readLong();
}
final private StoreImpl loadUpdatedStore() throws Exception{
if(status != UPDATE_POINTER) return this;
StoreImpl storeTemp = table.getStore( ((TableStorePage)storePage).con, filePosUpdated, type);
storeTemp.updatePointer = this;
return storeTemp;
}
private void resizePage(int minNewSize){
int newSize = Math.max(minNewSize, page.length*2);
byte[] newPage = new byte[newSize];
System.arraycopy( page, 0, newPage, 0, page.length);
page = newPage;
}
boolean isValidPage(){
return status == NORMAL || (status == UPDATED_PAGE && updatePointer != null);
}
int getUsedSize(){
return sizeUsed;
}
long getNextPagePos(){
if(updatePointer != null) return updatePointer.getNextPagePos();
if(nextPageOffset <= 0){
nextPageOffset = sizePhysical;
}
return filePos + nextPageOffset;
}
/**
*
* @param con Is needed to add this page to the commitPages. If it null then it save directly without rollback option.
* @return The file position if con == null.
* @throws SQLException
*/
long writeFinsh(SSConnection con) throws SQLException{
switch(type){
case SQLTokenizer.LONGVARBINARY:
case SQLTokenizer.INSERT:
case SQLTokenizer.CREATE:
sizeUsed = sizePhysical = offset;
break;
case SQLTokenizer.UPDATE:
if(status != UPDATE_POINTER) {
sizeUsed = offset;
break;
}
case SQLTokenizer.DELETE:
sizeUsed = PAGE_CONTROL_SIZE;
break;
//SQLTokenizer.SELECT should not occur here
default: throw new Error(""+type);
}
offset = 0;
writeInt( PAGE_MAGIC ); // for repair
writeInt( status);
writeInt( sizeUsed );
writeInt( sizePhysical );
writeInt( 0 ); //nextPageOffset
writeLong( filePosUpdated ); // Pointer of an updated page
storePage.setPageData( page, sizeUsed ); //TODO page sollte eigentlich beim einlesen gesetzt sein
if(con == null){
// the pointer is needed to safe in another page
// this produce not referenced pages on rollback
return storePage.commit();
}else{
return 0;
}
}
final private void createWriteLock() throws SQLException{
TableStorePage storePageWrite = table.requestWriteLock( ((TableStorePage)storePage).con, (TableStorePage)storePage );
if(storePageWrite == null)
throw SmallSQLException.create(Language.ROW_LOCKED);
storePage = storePageWrite;
}
/**
* Is call from updateRow().
* The offset of newData must be at the end of the data. It used as new page size.
*/
void updateFinsh(SSConnection con, StoreImpl newData) throws SQLException{
type = SQLTokenizer.UPDATE;
createWriteLock();
if(newData.offset <= sizePhysical || filePos == -1){
// the old page can be overwrite because it it large enough
page = newData.page; //newData is only a temp StoreImpl
offset = newData.offset;
if(sizePhysical < offset) sizePhysical = offset; // occur only on updates of not commited inserts (filePos == -1
writeFinsh(con);
}else{
// we need to create a new page because the old page is to small
newData.status = UPDATED_PAGE;
if(updatePointer == null){
// we need to create a new page and change the old page to a UPDATE_POINTER
((TableStorePage)newData.storePage).lockType = TableView.LOCK_INSERT;
filePosUpdated = newData.writeFinsh(null);
status = UPDATE_POINTER;
}else{
// we need to create a new page and delete the old page
((TableStorePage)newData.storePage).lockType = TableView.LOCK_INSERT;
updatePointer.filePosUpdated = newData.writeFinsh(null);
updatePointer.status = UPDATE_POINTER;
updatePointer.type = SQLTokenizer.UPDATE;
updatePointer.createWriteLock();
updatePointer.writeFinsh(con);
status = DELETED;
}
writeFinsh(con);
}
}
/*==============================================================================
Write und Read Methoden
==============================================================================*/
private int offset; // aktuelle read/write Position in der Page
int getCurrentOffsetInPage(){
return offset;
}
void setCurrentOffsetInPage(int newOffset){
this.offset = newOffset;
}
void writeByte( int value ){
int newSize = offset + 1;
if(newSize > page.length) resizePage(newSize);
page[ offset++ ] = (byte)(value);
}
int readByte(){
return page[ offset++ ];
}
int readUnsignedByte(){
return page[ offset++ ] & 0xFF;
}
void writeBoolean( boolean value ){
int newSize = offset + 1;
if(newSize > page.length) resizePage(newSize);
page[ offset++ ] = (byte)(value ? 1 : 0);
}
boolean readBoolean(){
return page[ offset++ ] != 0;
}
void writeShort( int value ){
int newSize = offset + 2;
if(newSize > page.length) resizePage(newSize);
page[ offset++ ] = (byte)(value >> 8);
page[ offset++ ] = (byte)(value);
}
int readShort(){
return (page[ offset++ ] << 8) | (page[ offset++ ] & 0xFF);
}
void writeInt( int value ){
int newSize = offset + 4;
if(newSize > page.length) resizePage(newSize);
page[ offset++ ] = (byte)(value >> 24);
page[ offset++ ] = (byte)(value >> 16);
page[ offset++ ] = (byte)(value >> 8);
page[ offset++ ] = (byte)(value);
}
int readInt(){
return ((page[ offset++ ]) << 24) |
((page[ offset++ ] & 0xFF) << 16) |
((page[ offset++ ] & 0xFF) << 8) |
((page[ offset++ ] & 0xFF));
}
void writeLong( long value ){
int newSize = offset + 8;
if(newSize >= page.length) resizePage(newSize);
page[ offset++ ] = (byte)(value >> 56);
page[ offset++ ] = (byte)(value >> 48);
page[ offset++ ] = (byte)(value >> 40);
page[ offset++ ] = (byte)(value >> 32);
page[ offset++ ] = (byte)(value >> 24);
page[ offset++ ] = (byte)(value >> 16);
page[ offset++ ] = (byte)(value >> 8);
page[ offset++ ] = (byte)(value);
}
long readLong(){
//return (((long)readInt()) << 32) | (readInt() & 0xFFFFFFFFL);
return ((long)(page[ offset++ ]) << 56) |
((long)(page[ offset++ ] & 0xFF) << 48) |
((long)(page[ offset++ ] & 0xFF) << 40) |
((long)(page[ offset++ ] & 0xFF) << 32) |
((long)(page[ offset++ ] & 0xFF) << 24) |
((page[ offset++ ] & 0xFF) << 16) |
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -