1356 lines
32 KiB
C
1356 lines
32 KiB
C
/*
|
|
* CDE - Common Desktop Environment
|
|
*
|
|
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
|
|
*
|
|
* These libraries and programs are free software; you can
|
|
* redistribute them and/or modify them under the terms of the GNU
|
|
* Lesser General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option)
|
|
* any later version.
|
|
*
|
|
* These libraries and programs are distributed in the hope that
|
|
* they 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 these libraries and programs; if not, write
|
|
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
|
|
* Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
/*
|
|
* $XConsortium: page_storage.C /main/8 1996/10/04 10:49:57 drk $
|
|
*
|
|
* Copyright (c) 1992 HAL Computer Systems International, Ltd.
|
|
* All rights reserved. Unpublished -- rights reserved under
|
|
* the Copyright Laws of the United States. USE OF A COPYRIGHT
|
|
* NOTICE IS PRECAUTIONARY ONLY AND DOES NOT IMPLY PUBLICATION
|
|
* OR DISCLOSURE.
|
|
*
|
|
* THIS SOFTWARE CONTAINS CONFIDENTIAL INFORMATION AND TRADE
|
|
* SECRETS OF HAL COMPUTER SYSTEMS INTERNATIONAL, LTD. USE,
|
|
* DISCLOSURE, OR REPRODUCTION IS PROHIBITED WITHOUT THE
|
|
* PRIOR EXPRESS WRITTEN PERMISSION OF HAL COMPUTER SYSTEMS
|
|
* INTERNATIONAL, LTD.
|
|
*
|
|
* RESTRICTED RIGHTS LEGEND
|
|
* Use, duplication, or disclosure by the Government is subject
|
|
* to the restrictions as set forth in subparagraph (c)(l)(ii)
|
|
* of the Rights in Technical Data and Computer Software clause
|
|
* at DFARS 252.227-7013.
|
|
*
|
|
* HAL COMPUTER SYSTEMS INTERNATIONAL, LTD.
|
|
* 1315 Dell Avenue
|
|
* Campbell, CA 95008
|
|
*
|
|
*/
|
|
|
|
|
|
#include "storage/page.h"
|
|
#include "storage/page_storage.h"
|
|
#include "storage/version.h"
|
|
#include "utility/db_version.h"
|
|
|
|
#define db_type "MMDB"
|
|
#define db_type_sz strlen(db_type)
|
|
|
|
#define db_order_sz sizeof(char)
|
|
|
|
int page_storage::dv_sz = 0;
|
|
int page_storage::abs_off = 0;
|
|
|
|
#ifndef C_API
|
|
page_cache_global_part page_storage::f_global_pcache;
|
|
#else
|
|
page_cache_global_part* page_storage::f_global_pcache_ptr = 0;
|
|
#endif
|
|
|
|
|
|
static char* db_version = 0;
|
|
static char* data_version = 0;
|
|
|
|
str_index_record_t::str_index_record_t(int offset, mmdb_pos_t lc)
|
|
: str_offset(offset), loc(lc)
|
|
{
|
|
}
|
|
|
|
void delete_str_index_record(const void* t)
|
|
{
|
|
delete ((str_index_record_t*)t);
|
|
}
|
|
|
|
extern void_ptr_array g_store_array;
|
|
extern Boolean g_transaction_on;
|
|
|
|
//////////////////////////////
|
|
//
|
|
//////////////////////////////
|
|
store_trans::store_trans(char* p, char* n, int psz) :
|
|
path(p), name(n), status(DISABLED),
|
|
log_store(0), log_index(0), max_pages(0), page_sz(psz)
|
|
{
|
|
}
|
|
|
|
store_trans::~store_trans()
|
|
{
|
|
/*
|
|
MESSAGE(cerr, "dstr store_trans object");
|
|
debug(cerr, status);
|
|
debug(cerr, DISABLED);
|
|
debug(cerr, int(log_store));
|
|
*/
|
|
|
|
if ( status == DISABLED && log_store ) {
|
|
delete log_store;
|
|
del_file(form("%s.log", name), path);
|
|
}
|
|
delete log_index;
|
|
}
|
|
|
|
void store_trans::init(rep_policy* policy)
|
|
{
|
|
if ( log_store == 0 ) {
|
|
log_store = new unixf_storage(path, form("%s.log", name), policy);
|
|
|
|
if ( ! (*log_store) )
|
|
throw(streamException(log_store -> rdstate()));
|
|
}
|
|
|
|
if ( log_index == 0 ) {
|
|
log_index = new imp_die;
|
|
}
|
|
}
|
|
|
|
void store_trans::quit()
|
|
{
|
|
if ( log_store )
|
|
log_store -> truncate(0);
|
|
|
|
if ( log_index )
|
|
log_index -> clean();
|
|
}
|
|
|
|
///////////////////////////////////
|
|
// Constructor
|
|
///////////////////////////////////
|
|
page_storage::page_storage( char* _path, char* _name,
|
|
unixf_storage* store, int pg_sz,
|
|
int/*no_pages*/, mmdb_byte_order_t create_order) :
|
|
abs_storage(_path, _name, PAGE_STORAGE_CODE),
|
|
trans_info(page_storage::path, page_storage::name, pg_sz),
|
|
page_sz(pg_sz),
|
|
f_local_pcache(30),
|
|
v_db_order(create_order), v_buf(0),
|
|
pagings(0),
|
|
total_page_access(0)
|
|
{
|
|
|
|
//debug(cerr, my_name());
|
|
//debug(cerr, (void*)this);
|
|
//#ifdef C_API
|
|
if ( dv_sz == 0 ) {
|
|
dv_sz = db_type_sz + mm_version::version_bytes() + db_order_sz;
|
|
abs_off = dv_sz;
|
|
}
|
|
//#endif
|
|
|
|
|
|
storage_ptr = store;
|
|
|
|
int bts = ((unixf_storage*)storage_ptr) -> bytes();
|
|
|
|
v_server_order = byte_order();
|
|
//debug(cerr, v_server_order);
|
|
|
|
if ( bts == 0 ) {
|
|
//////////////////////
|
|
// empty store file
|
|
//////////////////////
|
|
|
|
if ( db_version == 0 ) {
|
|
db_version = new char[dv_sz];
|
|
|
|
memcpy(db_version, db_type, db_type_sz);
|
|
|
|
mm_version x(MAJOR, MINOR);
|
|
|
|
x.to_byte_string(db_version+db_type_sz,
|
|
(v_db_order != v_server_order) ? true : false
|
|
);
|
|
|
|
char z = v_db_order;
|
|
memcpy(db_version+db_type_sz+mm_version::version_bytes(),
|
|
&z, db_order_sz);
|
|
}
|
|
|
|
storage_ptr -> updateString( 0, db_version, dv_sz, 0, true);
|
|
|
|
v_swap_order = (v_db_order != v_server_order) ? true : false;
|
|
|
|
} else {
|
|
///////////////////
|
|
// store exists.
|
|
///////////////////
|
|
|
|
bts -= abs_off;
|
|
|
|
if ( data_version == 0 ) {
|
|
data_version = new char[dv_sz];
|
|
}
|
|
|
|
storage_ptr -> readString(0, data_version, dv_sz);
|
|
|
|
/////////////////////////////////
|
|
// "MMDB" magic number testing
|
|
/////////////////////////////////
|
|
if ( memcmp(data_version, db_type, db_type_sz) != 0 ) {
|
|
throw(stringException(
|
|
form("bad magic number. corrupted store %s/%s", _path, _name)
|
|
)
|
|
);
|
|
}
|
|
|
|
///////////////////////////////
|
|
// get db byte order
|
|
///////////////////////////////
|
|
char z;
|
|
memcpy(&z,
|
|
data_version + db_type_sz + mm_version::version_bytes(),
|
|
db_order_sz
|
|
);
|
|
|
|
v_db_order = int(z);
|
|
|
|
if ( v_db_order != mmdb_big_endian && v_db_order != mmdb_little_endian ) {
|
|
debug(cerr, v_db_order);
|
|
throw(stringException(
|
|
"data is in neither big endian nor little endian format"
|
|
));
|
|
}
|
|
|
|
|
|
///////////////////////////////
|
|
// test version
|
|
///////////////////////////////
|
|
v_swap_order = (v_db_order != v_server_order) ? true : false;
|
|
|
|
mm_version dv(data_version+db_type_sz, v_swap_order);
|
|
|
|
f_version = dv;
|
|
|
|
mm_version cv(MAJOR, MINOR);
|
|
|
|
if ( cv < dv ) {
|
|
cerr << "code major = " << cv.major_version() << "\n";
|
|
cerr << "code minor = " << cv.minor_version() << "\n";
|
|
cerr << "data major = " << dv.major_version() << "\n";
|
|
cerr << "data minor = " << dv.minor_version() << "\n";
|
|
throw(stringException(
|
|
form("data and code version mismatch on store %s/%s", _path, _name)
|
|
)
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
//debug(cerr, v_db_order);
|
|
|
|
|
|
total_pages = bts / page_sz;
|
|
|
|
f_local_pcache.init_heap(this);
|
|
|
|
set_mode(HEALTH, true);
|
|
}
|
|
|
|
/***********************************************************/
|
|
// Destructor
|
|
/***********************************************************/
|
|
page_storage::~page_storage()
|
|
{
|
|
//MESSAGE(cerr, "dstr page_storage");
|
|
//debug(cerr, my_name());
|
|
|
|
delete v_buf;
|
|
delete storage_ptr;
|
|
|
|
delete data_version;
|
|
data_version = 0;
|
|
|
|
delete db_version;
|
|
db_version = 0;
|
|
|
|
f_local_pcache.quit_heap(this);
|
|
|
|
f_global_pcache.remove_pages(this);
|
|
|
|
/*
|
|
MESSAGE(cerr, my_name());
|
|
debug(cerr, total_page_access);
|
|
debug(cerr, pagings);
|
|
debug(cerr, float(pagings)/total_page_access);
|
|
*/
|
|
}
|
|
|
|
void page_storage::remove()
|
|
{
|
|
storage_ptr -> remove();
|
|
}
|
|
|
|
void page_storage::sync()
|
|
{
|
|
|
|
/*
|
|
MESSAGE(cerr, "page_storage::sync()");
|
|
debug(cerr, my_path());
|
|
debug(cerr, my_name());
|
|
debug(cerr, pages());
|
|
debug(cerr, int(this));
|
|
*/
|
|
|
|
|
|
long ind = f_global_pcache.f_replace_policy.first();
|
|
|
|
while ( ind != 0 ) {
|
|
lru_page *p= (lru_page*)(f_global_pcache.f_replace_policy)(ind, ACTIVE);
|
|
if ( p -> f_store == this )
|
|
sync( p );
|
|
f_global_pcache.f_replace_policy.next(ind);
|
|
}
|
|
|
|
//MESSAGE(cerr, "page_storage::sync() done");
|
|
}
|
|
|
|
void page_storage::sync( int page_num )
|
|
{
|
|
sync((*this)(page_num, READ));
|
|
}
|
|
|
|
void page_storage::sync( page* p )
|
|
{
|
|
if ( p == 0 || p -> page_id() <= 0 ) {
|
|
throw(stringException("null page pointer"));
|
|
}
|
|
|
|
if ( p -> dirty == true ) {
|
|
|
|
|
|
//cerr << "dirty PAGE swapped out " << p -> page_id();
|
|
//cerr << " " << my_name() << "\n";
|
|
|
|
int offset = ( p -> page_id() - 1 ) * page_sz ;
|
|
|
|
#ifdef PORTABLE_DB
|
|
p -> _swap_order(false);
|
|
#endif
|
|
|
|
//debug(cerr, *p);
|
|
|
|
#ifdef DEBUG
|
|
fprintf(stderr, "purging page at %p size=%d into %s/%s @ %s:%d\n",
|
|
p -> page_base(), page_sz,
|
|
storage_ptr->my_path(), storage_ptr->my_name(),
|
|
__FILE__, __LINE__);
|
|
#endif
|
|
storage_ptr -> updateString( abs_off + mmdb_pos_t(offset),
|
|
p -> page_base(),
|
|
page_sz, 0, true
|
|
);
|
|
#ifdef PORTABLE_DB
|
|
//////////////////////////////////////////////////////////////
|
|
// flip back to original. as the server will not quit after
|
|
// the sync.
|
|
//////////////////////////////////////////////////////////////
|
|
p -> _swap_order(true);
|
|
#endif
|
|
|
|
p -> dirty = false;
|
|
}
|
|
}
|
|
|
|
//static Boolean xflag = false;
|
|
/***********************************************************/
|
|
// readString().
|
|
/***********************************************************/
|
|
int
|
|
page_storage::readString(mmdb_pos_t loc, char* base, int len, int str_offset)
|
|
{
|
|
/*
|
|
int xstring_ofst = str_offset;
|
|
int xloc = loc;
|
|
int xlen = len;
|
|
|
|
MESSAGE(cerr, "page_storage: readString");
|
|
debug(cerr, my_path());
|
|
debug(cerr, my_name());
|
|
debug(cerr, loc);
|
|
debug(cerr, int(base));
|
|
debug(cerr, len);
|
|
debug(cerr, str_offset);
|
|
*/
|
|
|
|
buffer in_cache(0);
|
|
in_cache.set_chunk(base, len);
|
|
|
|
spointer_t *slot_info;
|
|
|
|
while ( len > 0 ) {
|
|
|
|
if ( loc == 0 ) {
|
|
throw(stringException("damaged store."));
|
|
}
|
|
|
|
int page_num = PAGE_ID( loc, page_sz );
|
|
int page_slot = PAGE_IDX( loc, page_sz );
|
|
|
|
//debug(cerr, page_num);
|
|
//debug(cerr, page_slot);
|
|
|
|
page *y = (*this)(page_num, READ);
|
|
|
|
slot_info = y -> get_spointer(page_slot);
|
|
|
|
int str_leng = slot_info -> string_leng();
|
|
loc = slot_info -> forward_ptr();
|
|
/*
|
|
MESSAGE(cerr, "slot info:");
|
|
debug(cerr, str_leng);
|
|
debug(cerr, loc);
|
|
debug(cerr, slot_info -> string_ofst());
|
|
*/
|
|
|
|
delete slot_info;
|
|
|
|
if ( str_offset >= str_leng ) {
|
|
|
|
str_offset -= str_leng;
|
|
|
|
} else {
|
|
int bytes_read = MIN(len, str_leng - str_offset);
|
|
|
|
/*
|
|
debug(cerr, len);
|
|
debug(cerr, bytes_read);
|
|
*/
|
|
|
|
y -> get( page_slot, in_cache, str_offset, bytes_read );
|
|
|
|
len -= bytes_read;
|
|
|
|
str_offset = 0;
|
|
}
|
|
}
|
|
|
|
//MESSAGE(cerr, "page_storage:: readString done");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
page_storage::get_str_ptr(mmdb_pos_t loc, char*& str,
|
|
int& len)
|
|
{
|
|
int page_num = PAGE_ID( loc, page_sz );
|
|
int page_slot = PAGE_IDX( loc, page_sz );
|
|
|
|
|
|
//MESSAGE(cerr, "IN get_str_ptr()");
|
|
//debug(cerr, my_path());
|
|
//debug(cerr, my_name());
|
|
//debug(cerr, page_num);
|
|
//debug(cerr, page_slot);
|
|
|
|
|
|
page *y = (*this)(page_num, READ);
|
|
|
|
spointer_t* slot_info = y -> get_spointer(page_slot);
|
|
|
|
//debug(cerr, int(slot_info -> get_mode(spointer_t::DELETED)));
|
|
/*
|
|
if ( page_num == 1 && page_slot == 1 ) {
|
|
debug(cerr, hex(slot_info -> header.int_view));
|
|
debug(cerr, hex(slot_info -> header.bit_view.spointer));
|
|
debug(cerr, hex(slot_info -> header.bit_view.length));
|
|
debug(cerr, hex(slot_info -> header.bit_view.deleted));
|
|
debug(cerr, hex(slot_info -> header.bit_view.first_recd));
|
|
debug(cerr, hex(slot_info -> header.bit_view.updated));
|
|
debug(cerr, hex(slot_info -> header.bit_view.is_object));
|
|
}
|
|
*/
|
|
|
|
|
|
if ( slot_info -> get_mode(spointer_t::DELETED) == true ) {
|
|
delete slot_info;
|
|
throw(stringException("read deleted byte string"));
|
|
}
|
|
|
|
delete slot_info;
|
|
y -> get_str_ptr(page_slot, str, len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int page_storage::insertString(mmdb_pos_t& loc, const char* base, int len, Boolean flush_opt)
|
|
{
|
|
/*
|
|
cerr << "insert: len=" << len << "\n";
|
|
int u3 = len;
|
|
*/
|
|
|
|
|
|
#ifdef STORAGE_DEBUG
|
|
MESSAGE(cerr, "page_storage insertString");
|
|
debug(cerr, len);
|
|
#endif
|
|
|
|
buffer temp_buf(0);
|
|
int page_num = 0;
|
|
int page_oft = 0;
|
|
|
|
int str_offset = len;
|
|
int sec_len;
|
|
|
|
Boolean first_recd = true;
|
|
Boolean new_page;
|
|
|
|
while ( str_offset > 0 ) {
|
|
|
|
loc = (page_num != 0 ) ?
|
|
FORM_PAGE_REF(page_num, page_oft, page_sz) :
|
|
0;
|
|
|
|
fbytes_t* v = f_local_pcache.find_host_page(this, new_page) ;
|
|
page_num = v -> page_num;
|
|
|
|
page* y = (*this)(page_num, WRITE);
|
|
|
|
sec_len = MIN(len, y -> free_bytes());
|
|
|
|
str_offset -= sec_len;
|
|
|
|
|
|
temp_buf.set_chunk((char*)(base + str_offset), sec_len);
|
|
temp_buf.set_content_sz(sec_len);
|
|
|
|
y -> put( page_oft, temp_buf );
|
|
|
|
spointer_t *x = y -> get_spointer(page_oft);
|
|
x -> set_mode(spointer_t::FIRST_RECD, first_recd);
|
|
x -> set_forward_ptr(loc);
|
|
delete x;
|
|
|
|
v -> free_bytes = y -> free_bytes();
|
|
f_local_pcache.adjust_heap(v, new_page);
|
|
|
|
if ( flush_opt == true ) sync(page_num);
|
|
|
|
first_recd = false;
|
|
len -= sec_len;
|
|
}
|
|
|
|
|
|
#ifdef STORAGE_DEBUG
|
|
MESSAGE(cerr, "FINAL LOC COMPONENT");
|
|
debug(cerr, page_oft);
|
|
debug(cerr, page_sz);
|
|
debug(cerr, page_num);
|
|
#endif
|
|
|
|
loc = FORM_PAGE_REF(page_num, page_oft, page_sz);
|
|
|
|
|
|
|
|
/*
|
|
int u1 = PAGE_ID( loc, page_sz );
|
|
int u2 = PAGE_IDX( loc, page_sz );
|
|
cerr << "insert:" << page_num << "." << page_oft << " " << u3 << "\n";
|
|
*/
|
|
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************/
|
|
// updateString().
|
|
/***********************************************************/
|
|
int
|
|
page_storage::updateString(mmdb_pos_t loc, const char* base, int len,
|
|
int string_ofst, Boolean flush_opt)
|
|
{
|
|
/*
|
|
int u1 = PAGE_ID( loc, page_sz );
|
|
int u2 = PAGE_IDX( loc, page_sz );
|
|
cerr << "update:" << u1 << "." << u2 << " " << len << " " << string_ofst << "\n";
|
|
*/
|
|
|
|
|
|
/*
|
|
MESSAGE(cerr, "update string");
|
|
debug(cerr, name);
|
|
debug(cerr, loc);
|
|
*/
|
|
|
|
int page_num =0;
|
|
int slot_num =0;
|
|
int offset = 0;
|
|
page* y = 0;
|
|
spointer_t *x = 0;
|
|
|
|
while ( len > 0 && loc != 0 ) {
|
|
|
|
page_num = PAGE_ID( loc, page_sz );
|
|
slot_num = PAGE_IDX( loc, page_sz );
|
|
//debug(cerr, page_num);
|
|
//debug(cerr, slot_num);
|
|
|
|
y = (*this)(page_num, WRITE);
|
|
|
|
x = y -> get_spointer(slot_num);
|
|
|
|
/*
|
|
debug(cerr, x -> forward_ptr());
|
|
debug(cerr, x -> string_leng());
|
|
debug(cerr, x -> string_ofst());
|
|
*/
|
|
|
|
loc = x -> forward_ptr();
|
|
int this_len = x -> string_leng();
|
|
delete x;
|
|
|
|
if ( string_ofst >= this_len ) {
|
|
string_ofst -= this_len;
|
|
continue;
|
|
}
|
|
|
|
int update_len = MIN(len, this_len - string_ofst);
|
|
|
|
buffer sbuf(0);
|
|
sbuf.set_chunk((char*)(base+offset), update_len);
|
|
sbuf.set_content_sz(update_len);
|
|
|
|
y -> update_slot(slot_num, sbuf, string_ofst);
|
|
|
|
if ( flush_opt == true ) sync(page_num);
|
|
|
|
string_ofst = 0;
|
|
offset += update_len;
|
|
len -= update_len;
|
|
}
|
|
|
|
if ( len > 0 ) {
|
|
|
|
//debug(cerr, len);
|
|
//debug(cerr, offset);
|
|
|
|
/*****************************/
|
|
// the update is an expanding
|
|
/*****************************/
|
|
mmdb_pos_t new_loc;
|
|
insertString(new_loc, base+offset, len, flush_opt) ;
|
|
|
|
y = (*this)(page_num, WRITE);
|
|
x = y -> get_spointer(slot_num);
|
|
x -> set_forward_ptr(new_loc);
|
|
delete x;
|
|
|
|
page_num = PAGE_ID( new_loc, page_sz );
|
|
slot_num = PAGE_IDX( new_loc, page_sz );
|
|
y = (*this)(page_num, WRITE);
|
|
x = y -> get_spointer(slot_num);
|
|
x -> set_mode(spointer_t::FIRST_RECD, false);
|
|
delete x;
|
|
|
|
y -> dirty = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// if ( loc != 0 ) {
|
|
|
|
/*****************************/
|
|
// the update is a shrinking
|
|
/*****************************/
|
|
// y = (*this)(page_num);
|
|
// x = y -> get_spointer(slot_num);
|
|
// x -> set_forward_ptr(0);
|
|
// return deleteString(loc);
|
|
// }
|
|
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************/
|
|
// deleteString().
|
|
/***********************************************************/
|
|
int page_storage::deleteString(mmdb_pos_t loc, Boolean flush_opt)
|
|
{
|
|
while ( loc != 0 ) {
|
|
int page_num = PAGE_ID( loc, page_sz );
|
|
int slot_num = PAGE_IDX( loc, page_sz );
|
|
//debug(cerr, page_num);
|
|
//debug(cerr, slot_num);
|
|
|
|
page* y = (*this)(page_num, WRITE);
|
|
|
|
spointer_t *x = y -> get_spointer(slot_num);
|
|
|
|
if ( x -> get_mode(spointer_t::DELETED) == true ) {
|
|
delete x;
|
|
return 0;
|
|
}
|
|
|
|
loc = x -> forward_ptr();
|
|
//debug(cerr, loc);
|
|
delete x;
|
|
|
|
y -> del_slot(slot_num);
|
|
|
|
if ( flush_opt == true ) sync(page_num);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************/
|
|
// allocate a chunk on a page.
|
|
/***********************************************************/
|
|
int
|
|
page_storage::allocString(mmdb_pos_t& loc, int len, char*& string_ptr, int mode)
|
|
{
|
|
//MESSAGE(cerr, "AllocString:");
|
|
//debug(cerr, my_name());
|
|
//debug(cerr, len);
|
|
|
|
if ( len > page_sz ) {
|
|
MESSAGE(cerr, "allocString(): string too long");
|
|
throw(boundaryException(0, page_sz, len));
|
|
}
|
|
|
|
Boolean new_page;
|
|
|
|
fbytes_t* v = f_local_pcache.find_host_page(this, new_page, len) ;
|
|
|
|
page* x = (*this)(v -> page_num, WRITE);
|
|
|
|
int slot_num;
|
|
|
|
x->alloc_slot(slot_num, len, string_ptr);
|
|
|
|
spointer_t* slot_info = x -> get_spointer(slot_num);
|
|
slot_info -> add_mode(mode);
|
|
delete slot_info;
|
|
|
|
|
|
v -> free_bytes = x -> free_bytes();
|
|
f_local_pcache.adjust_heap(v, new_page);
|
|
|
|
loc = FORM_PAGE_REF(v -> page_num, slot_num, page_sz);
|
|
|
|
#ifdef STORAGE_DEBUG
|
|
MESSAGE(cerr, "in allocString(): final params");
|
|
debug(cerr, int(x -> dirty));
|
|
debug(cerr, page_num);
|
|
debug(cerr, slot_num);
|
|
debug(cerr, my_name());
|
|
debug(cerr, loc);
|
|
debug(cerr, int(string_ptr));
|
|
#endif
|
|
|
|
|
|
// int u1 = PAGE_ID( loc, page_sz );
|
|
// int u2 = PAGE_IDX( loc, page_sz );
|
|
// cerr << "Allocated ID=" << u1 << "." << u2 << " " << len << "\n";
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int page_storage::appendString(mmdb_pos_t loc, const char* base, int len, Boolean)
|
|
{
|
|
/*
|
|
int u1 = PAGE_ID( loc, page_sz );
|
|
int u2 = PAGE_IDX( loc, page_sz );
|
|
cerr << "append:" << u1 << "." << u2 << " " << len << "\n";
|
|
*/
|
|
|
|
|
|
|
|
int page_num = 0;
|
|
int slot_num = 0;
|
|
page* y;
|
|
spointer_t *x;
|
|
|
|
while ( loc != 0 ) {
|
|
|
|
page_num = PAGE_ID( loc, page_sz );
|
|
slot_num = PAGE_IDX( loc, page_sz );
|
|
|
|
y = (*this)(page_num, READ);
|
|
x = y -> get_spointer(slot_num);
|
|
|
|
loc = x -> forward_ptr();
|
|
delete x;
|
|
}
|
|
|
|
insertString(loc, base, len) ;
|
|
|
|
y = (*this)(page_num, WRITE);
|
|
x = y -> get_spointer(slot_num);
|
|
x -> set_forward_ptr(loc);
|
|
delete x;
|
|
|
|
page_num = PAGE_ID( loc, page_sz );
|
|
slot_num = PAGE_IDX( loc, page_sz );
|
|
y = (*this)(page_num, WRITE);
|
|
x = y -> get_spointer(slot_num);
|
|
x -> set_mode(spointer_t::FIRST_RECD, false);
|
|
delete x;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************/
|
|
// create new page frame.
|
|
/***********************************************************/
|
|
int page_storage::add_page_frames(int pages)
|
|
{
|
|
total_pages += pages;
|
|
|
|
//////////////////////////////////////
|
|
// prepare the new page in the cache
|
|
//////////////////////////////////////
|
|
for (int i=1; i<=pages; i++)
|
|
operator()(total_pages+i-pages, WRITE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************/
|
|
// return the mmdb_pos_t of the first page.
|
|
/***********************************************************/
|
|
int page_storage::first() const
|
|
{
|
|
return ( total_pages > 0 ) ? 1 : 0;
|
|
}
|
|
|
|
//////////////////////////////////////////
|
|
/* ind should be the page id */
|
|
//////////////////////////////////////////
|
|
page* page_storage::operator()(int ind, enum access mode)
|
|
{
|
|
/*
|
|
if ( mode == WRITE ) {
|
|
MESSAGE(cerr, "opera(): write mode");
|
|
debug(cerr, ind);
|
|
debug(cerr, name);
|
|
}
|
|
*/
|
|
|
|
/*
|
|
if ( mode == WRITE ) {
|
|
char* s = name + strlen(name) - 11;
|
|
debug(cerr, s);
|
|
if ( strcmp(s, "index.store") == 0 )
|
|
debug(cerr, name);
|
|
}
|
|
*/
|
|
|
|
//cerr << "Try to get page " << ind << " from " << my_name() << "\n";
|
|
|
|
if ( ! INRANGE( ind, 1, pages() ) ) {
|
|
debug(cerr, my_path());
|
|
debug(cerr, my_name());
|
|
MESSAGE(cerr,
|
|
form("page_storage:: operator(): mmdb_pos_t %d out of range.", ind)
|
|
);
|
|
throw(boundaryException(1, pages(), ind));
|
|
}
|
|
|
|
page* p = f_local_pcache.in_cache(this, ind);
|
|
|
|
if ( p == 0 ) {
|
|
|
|
//cerr << "swapping in a page " << ind << endl;
|
|
p = f_global_pcache.load_new_page( this, ind,
|
|
(v_db_order==v_server_order) ? false : true
|
|
);
|
|
|
|
}
|
|
|
|
/*
|
|
debug(cerr, my_name());
|
|
debug(cerr, p -> count());
|
|
debug(cerr, int(mode));
|
|
debug(cerr, int(WRITE));
|
|
*/
|
|
if ( mode == WRITE ) {
|
|
p -> dirty = true;
|
|
save_to_log(p);
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
/***********************************************************/
|
|
// update ind to the mmdb_pos_t of next page.
|
|
/***********************************************************/
|
|
void page_storage::next(int& ind) const
|
|
{
|
|
if ( ind >= pages() )
|
|
ind = 0;
|
|
else
|
|
ind++;
|
|
}
|
|
|
|
io_status page_storage::asciiOut(ostream& out)
|
|
{
|
|
int ind = first();
|
|
while ( ind != 0 ) {
|
|
#ifdef DEBUG
|
|
page* p = (*this)(ind, READ);
|
|
debug(out, *p);
|
|
#endif
|
|
next(ind);
|
|
}
|
|
return done;
|
|
|
|
/*
|
|
debug(cerr, pagings);
|
|
pagings = 0;
|
|
*/
|
|
}
|
|
|
|
Boolean
|
|
page_storage::seek_loc(mmdb_pos_t& loc, const direction d, int smd)
|
|
{
|
|
if ( d == positive ) {
|
|
return seek_loc_positive(loc, smd);
|
|
} else {
|
|
return seek_loc_negative(loc, smd);
|
|
}
|
|
}
|
|
|
|
Boolean page_storage::seek_loc_positive(mmdb_pos_t& loc, int smd)
|
|
{
|
|
int pgs = pages();
|
|
|
|
int page_num = PAGE_ID( loc, page_sz );
|
|
int page_slot = PAGE_IDX( loc, page_sz );
|
|
|
|
while ( page_num <= pgs ) {
|
|
|
|
page* x = (*this)(page_num, READ);
|
|
|
|
if ( page_slot == 0 )
|
|
page_slot = x -> first();
|
|
else
|
|
x -> next( page_slot );
|
|
|
|
spointer_t *y;
|
|
|
|
while ( page_slot && (y = x -> get_spointer(page_slot)) != 0 ) {
|
|
if ( y -> get_mode(spointer_t::DELETED) == false &&
|
|
y -> test_mode(smd) == true ) {
|
|
loc = FORM_PAGE_REF(page_num, page_slot, page_sz);
|
|
delete y;
|
|
return true;
|
|
} else {
|
|
delete y;
|
|
x -> next(page_slot);
|
|
}
|
|
}
|
|
|
|
page_num++;
|
|
page_slot = 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
Boolean page_storage::seek_loc_negative(mmdb_pos_t& loc, int smd)
|
|
{
|
|
int page_num = PAGE_ID( loc, page_sz );
|
|
int page_slot = PAGE_IDX( loc, page_sz );
|
|
|
|
while ( page_num > 0 ) {
|
|
|
|
page* x = (*this)(page_num, READ);
|
|
|
|
if ( page_slot == 0 )
|
|
page_slot = x -> count() - 1;
|
|
else
|
|
x -> prev(page_slot);
|
|
|
|
spointer_t *y;
|
|
|
|
while ( page_slot && (y = x -> get_spointer(page_slot)) != 0 )
|
|
{
|
|
if ( y -> get_mode(spointer_t::DELETED) == false &&
|
|
y -> test_mode(smd) == true ) {
|
|
loc = FORM_PAGE_REF(page_num, page_slot, page_sz);
|
|
delete y;
|
|
return true;
|
|
} else {
|
|
delete y;
|
|
x -> prev(page_slot);
|
|
}
|
|
}
|
|
|
|
page_num--;
|
|
page_slot = 0;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void page_storage::reset_paging_count()
|
|
{
|
|
/*
|
|
if ( storage_ptr -> rdbuf() -> is_open() ) {
|
|
cerr << storage_ptr -> my_name() << " has not been closed\n";
|
|
storage_ptr -> close();
|
|
} else {
|
|
cerr << storage_ptr -> my_name() << " is already closed\n";
|
|
}
|
|
*/
|
|
pagings = 0;
|
|
|
|
}
|
|
|
|
int page_storage::paging_count() const
|
|
{
|
|
return pagings;
|
|
}
|
|
|
|
mmdb_pos_t page_storage::first_loc()
|
|
{
|
|
if ( pages() >= 1 && (*this)(1, READ) -> count() != 0 )
|
|
return FORM_PAGE_REF(1, 1, page_sz);
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
mmdb_pos_t page_storage::last_loc()
|
|
{
|
|
int pgs = pages();
|
|
|
|
if ( pgs == 0 )
|
|
return 0;
|
|
|
|
for ( int i=pgs; i>0; i-- ) {
|
|
int ct = (*this)(i, READ) -> count();
|
|
if ( ct > 0 )
|
|
return FORM_PAGE_REF(i, ct, page_sz);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
page_storage::get_str_locs(mmdb_pos_t str_loc,
|
|
str_index_record_tPtr*& vector, int& vector_len
|
|
)
|
|
{
|
|
int vector_sz = 20;
|
|
vector = new str_index_record_tPtr[vector_sz];
|
|
|
|
int num_pieces = 0;
|
|
int str_offset = 0;
|
|
spointer_t *x = 0;
|
|
|
|
while ( str_loc > 0 ) {
|
|
|
|
if ( num_pieces >= vector_sz ) {
|
|
vector_sz *= 2;
|
|
vector = (str_index_record_tPtr*)realloc(
|
|
(char*)vector,
|
|
vector_sz*sizeof(str_index_record_tPtr)
|
|
);
|
|
}
|
|
|
|
vector[num_pieces++] =
|
|
new str_index_record_t(str_offset, str_loc);
|
|
|
|
int page_num = PAGE_ID( str_loc, page_sz );
|
|
int page_slot = PAGE_IDX( str_loc, page_sz );
|
|
|
|
page *y = (*this)(page_num, READ);
|
|
|
|
x = y -> get_spointer(page_slot);
|
|
|
|
str_offset += x -> string_leng();
|
|
str_loc = x -> forward_ptr();
|
|
|
|
delete x;
|
|
}
|
|
|
|
vector_len = num_pieces;
|
|
return 0;
|
|
}
|
|
|
|
int page_storage::set_page_dirty(mmdb_pos_t loc)
|
|
{
|
|
MESSAGE(cerr, "set page dirty: page loc is");
|
|
debug(cerr, loc);
|
|
|
|
page* x = (*this)(PAGE_ID( loc, page_sz ), WRITE);
|
|
x -> dirty = true;
|
|
return 0;
|
|
}
|
|
|
|
Boolean page_storage::io_mode(int mode)
|
|
{
|
|
if ( storage_ptr )
|
|
return storage_ptr -> io_mode(mode);
|
|
else
|
|
return false;
|
|
}
|
|
|
|
void page_storage::begin_trans()
|
|
{
|
|
mtry {
|
|
if ( trans_info.status == store_trans::ENABLED )
|
|
throw(stringException("trans is still in progress"));
|
|
|
|
trans_info.init(policy);
|
|
trans_info.set_max_pages(total_pages);
|
|
|
|
trans_info.status = store_trans::ENABLED;
|
|
|
|
int l_max_pages = trans_info.max_pages;
|
|
if ( swap_order() == true )
|
|
ORDER_SWAP_UINT(l_max_pages);
|
|
|
|
trans_info.log_store ->
|
|
updateString(0, (char*)&l_max_pages, sizeof(l_max_pages), 0, true);
|
|
}
|
|
|
|
mcatch (mmdbException&, e) {
|
|
// workaround for solaris's /SUNWspro/bin/CC compiler.
|
|
beginTransException x;
|
|
throw(x);
|
|
//throw(beginTransException());
|
|
}
|
|
end_try;
|
|
}
|
|
|
|
void page_storage::commit_trans()
|
|
{
|
|
//////////////////////////////
|
|
// sync the touched pages
|
|
//////////////////////////////
|
|
mtry {
|
|
int ind = trans_info.log_index -> first_bucket();
|
|
|
|
while ( ind != -1 ) {
|
|
|
|
imp_bucket* bucket = trans_info.log_index -> get_bucket(ind);
|
|
|
|
if ( bucket ) {
|
|
long b_ind = bucket -> first();
|
|
while ( b_ind != 0 ) {
|
|
data_t* z = (*bucket)(b_ind);
|
|
//debug(cerr, ((page*)(z -> dt)) -> count());
|
|
sync((page*)(z -> dt));
|
|
bucket -> next(b_ind);
|
|
}
|
|
}
|
|
trans_info.log_index -> next_bucket(ind);
|
|
}
|
|
|
|
trans_info.quit();
|
|
|
|
trans_info.status = store_trans::DISABLED;
|
|
}
|
|
|
|
mcatch (mmdbException &,e) {
|
|
// workaround for solaris's /SUNWspro/bin/CC compiler.
|
|
commitTransException x;
|
|
throw(x);
|
|
} end_try;
|
|
}
|
|
|
|
void page_storage::roll_back()
|
|
{
|
|
mtry
|
|
{
|
|
if ( exist_file(form("%s.log", name), path) == false )
|
|
return;
|
|
|
|
MESSAGE(cerr, "roll_back() begins");
|
|
|
|
trans_info.init(policy); // init the log store
|
|
|
|
//////////////////////////////////
|
|
// verify the log is in good shape
|
|
//////////////////////////////////
|
|
int m;
|
|
unsigned int log_bytes = trans_info.log_store -> bytes();
|
|
|
|
if ( trans_info.log_store &&
|
|
(*trans_info.log_store) &&
|
|
log_bytes > sizeof(m) )
|
|
{
|
|
|
|
//////////////////////////////////
|
|
// truncate the store to previous
|
|
// length
|
|
//////////////////////////////////
|
|
trans_info.log_store -> readString(0, (char*)&m, sizeof(m));
|
|
|
|
if ( swap_order() == true )
|
|
ORDER_SWAP_UINT(m);
|
|
|
|
trans_info.set_max_pages(m); // init the log store
|
|
|
|
((unixf_storage*)storage_ptr) -> truncate(abs_off + m*page_sz);
|
|
|
|
//debug(cerr, m);
|
|
//////////////////////////////////
|
|
// restore the store to previous
|
|
// state
|
|
//////////////////////////////////
|
|
int l_pid = 0;
|
|
|
|
if ( (log_bytes - sizeof(m)) % (page_sz+sizeof(l_pid)) != 0 )
|
|
throw(stringException("corrupted transaction log"));
|
|
|
|
int u = (log_bytes - sizeof(m)) / (page_sz+sizeof(l_pid));
|
|
//debug(cerr, u);
|
|
|
|
|
|
buffer log_buf(page_sz);
|
|
for ( int i=0; i<u; i++ ) {
|
|
|
|
int offset = sizeof(m) + i*(page_sz + sizeof(l_pid));
|
|
|
|
trans_info.log_store ->
|
|
readString(offset, // page id
|
|
(char*)&l_pid, sizeof(l_pid)
|
|
);
|
|
|
|
if ( swap_order() == true ) // swap byte order if necessary
|
|
ORDER_SWAP_UINT(l_pid);
|
|
|
|
trans_info.log_store ->
|
|
readString(offset + sizeof(l_pid), // page content
|
|
log_buf.get_base(),
|
|
page_sz
|
|
);
|
|
|
|
|
|
storage_ptr -> updateString( abs_off + (l_pid-1)*page_sz,
|
|
log_buf.get_base(),
|
|
page_sz,
|
|
0,
|
|
true
|
|
);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// make sure the cached pages are not synced
|
|
///////////////////////////////////////////////
|
|
|
|
long ind = f_global_pcache.f_replace_policy.first();
|
|
|
|
while ( ind != 0 ) {
|
|
lru_page *p = (lru_page*)
|
|
(f_global_pcache.f_replace_policy)(ind, ACTIVE);
|
|
|
|
if ( p -> f_store == this )
|
|
p -> dirty = false;
|
|
f_global_pcache.f_replace_policy.next(ind);
|
|
}
|
|
|
|
trans_info.quit(); // remove the log store
|
|
|
|
trans_info.status = store_trans::DISABLED;
|
|
}
|
|
|
|
mcatch (mmdbException &,e)
|
|
{
|
|
// workaround for solaris's /SUNWspro/bin/CC compiler.
|
|
rollbackTransException x;
|
|
throw(x);
|
|
} end_try;
|
|
|
|
MESSAGE(cerr, "roll_back() completes");
|
|
}
|
|
|
|
void page_storage::save_to_log(page* p)
|
|
{
|
|
/*
|
|
MESSAGE(cerr, "About to save to log");
|
|
debug(cerr, my_name());
|
|
debug(cerr, int(trans_info.status));
|
|
debug(cerr, int(store_trans::ENABLED));
|
|
debug(cerr, trans_info.max_pages);
|
|
*/
|
|
|
|
if ( trans_info.status == store_trans::ENABLED &&
|
|
INRANGE(p -> page_id(), 1, trans_info.max_pages)
|
|
)
|
|
{
|
|
//assert ( trans_info.log_store );
|
|
//assert ( trans_info.log_index );
|
|
|
|
if ( trans_info.log_store == 0 || trans_info.log_index == 0 )
|
|
throw(stringException("corrupted store"));
|
|
|
|
int l_pid = p -> page_id();
|
|
data_t pkey(l_pid, voidPtr(p));
|
|
|
|
|
|
if ( trans_info.log_index -> member(pkey) == false ) {
|
|
//MESSAGE(cerr, form("Save_to_log pid=%d, pcnt = %d, name=%s",
|
|
// l_pid, p -> count(), name));
|
|
|
|
int log_bytes_before = trans_info.log_store -> bytes();
|
|
|
|
mtry {
|
|
if ( swap_order() == true ) // swap to desired order
|
|
ORDER_SWAP_UINT(l_pid);
|
|
|
|
trans_info.log_store ->
|
|
appendString( 0, (char*)&l_pid, sizeof(l_pid), false );
|
|
|
|
if ( swap_order() == true ) // swap back
|
|
ORDER_SWAP_UINT(l_pid);
|
|
|
|
p -> _swap_order(false);
|
|
|
|
trans_info.log_store ->
|
|
appendString( 0, p -> page_base(), page_sz, true );
|
|
|
|
//debug(cerr, trans_info.log_store -> bytes());
|
|
p -> _swap_order(true);
|
|
|
|
trans_info.log_index -> insert(pkey);
|
|
}
|
|
|
|
mcatch (mmdbException&, e) {
|
|
trans_info.log_store -> truncate(log_bytes_before);
|
|
rethrow;
|
|
}
|
|
end_try;
|
|
|
|
}
|
|
// else
|
|
//MESSAGE(cerr, form("Not save_to_log pid=%d, name=%s", l_pid, name));
|
|
|
|
}
|
|
}
|
|
|
|
buffer& page_storage::aux_buf()
|
|
{
|
|
if ( v_buf == 0 )
|
|
v_buf = new buffer(LBUFSIZ);
|
|
return *v_buf;
|
|
}
|
|
|