Created by Scott Robert Ladd at Coyote Gulch Productions.
00001 //--------------------------------------------------------------------- 00002 // Algorithmic Conjurings @ http://www.coyotegulch.com 00003 // 00004 // Itzam - An Embedded Database Engine 00005 // 00006 // database_file.h 00007 // 00008 //--------------------------------------------------------------------- 00009 // 00010 // Copyright 2004, 2005, 2006 Scott Robert Ladd 00011 // 00012 // This program is free software; you can redistribute it and/or modify 00013 // it under the terms of the GNU General Public License as published by 00014 // the Free Software Foundation; either version 2 of the License, or 00015 // (at your option) any later version. 00016 // 00017 // This program is distributed in the hope that it will be useful, 00018 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00019 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00020 // GNU General Public License for more details. 00021 // 00022 // You should have received a copy of the GNU General Public License 00023 // along with this program; if not, write to the 00024 // Free Software Foundation, Inc. 00025 // 59 Temple Place - Suite 330 00026 // Boston, MA 02111-1307, USA. 00027 // 00028 //----------------------------------------------------------------------- 00029 // 00030 // For more information on this software package, please visit 00031 // Scott's web site, Coyote Gulch Productions, at: 00032 // 00033 // http://www.coyotegulch.com 00034 // 00035 //----------------------------------------------------------------------- 00036 00037 #if !defined(LIBITZAM_CPP_COMPLEX_DATABASE_H) 00038 #define LIBITZAM_CPP_COMPLEX_DATABASE_H 00039 00040 // Standard C++ 00041 #include <set> 00042 #include <vector> 00043 00044 // include common classes 00045 #include "itzam_common.h" 00046 00048 00053 namespace itzam 00054 { 00055 using std::set; 00056 using std::vector; 00057 00059 00063 class ref_list 00064 { 00065 private: 00067 set<itzam_ref> m_refs; 00068 00070 static const size_t BLOCK_SIZE = 256; 00071 00072 public: 00074 00077 ref_list() 00078 : m_refs() 00079 { 00080 // nada 00081 } 00082 00084 00087 ref_list(const set<itzam_ref> & value) 00088 : m_refs(value) 00089 { 00090 // nada 00091 } 00092 00094 00101 ref_list(const serial_data & source) 00102 : m_refs() 00103 { 00104 size_t n = 0; 00105 const itzam_ref * p = reinterpret_cast<const itzam_ref *>(source.get_data()); 00106 00107 while ((n < source.get_size()) && (*p != ITZAM_NULL_REF)) 00108 { 00109 m_refs.insert(*p); 00110 n += sizeof(itzam_ref); 00111 ++p; 00112 } 00113 } 00114 00116 00121 ref_list(const ref_list & source) 00122 : m_refs(source.m_refs) 00123 { 00124 // nada 00125 } 00126 00128 00134 ref_list & operator = (const ref_list & source) 00135 { 00136 m_refs = source.m_refs; 00137 return *this; 00138 } 00139 00141 00147 ref_list & operator = (const set<itzam_ref> & source) 00148 { 00149 m_refs = source; 00150 return *this; 00151 } 00152 00154 00160 ref_list & operator = (const serial_data & source) 00161 { 00162 m_refs = *((set<itzam_ref> *)source.get_data()); 00163 return *this; 00164 } 00165 00167 00172 ~ref_list() 00173 { 00174 // nada, just for any derived classes 00175 } 00176 00178 00182 operator set<itzam_ref>() const 00183 { 00184 return m_refs; 00185 } 00186 00188 00193 operator serial_data() const 00194 { 00195 size_t size = (m_refs.size() / BLOCK_SIZE + 1) * BLOCK_SIZE; 00196 00197 if (size == 0) 00198 size = BLOCK_SIZE; 00199 00200 itzam_ref * refs = (itzam_ref *)malloc(sizeof(itzam_ref) * size); 00201 itzam_ref * p = refs; 00202 00203 size_t n = 0; 00204 for (set<itzam_ref>::const_iterator i = m_refs.begin(); i != m_refs.end(); ++i) 00205 { 00206 *p = *i; 00207 ++p; 00208 ++n; 00209 } 00210 00211 for (; n < size; ++n) 00212 { 00213 *p = ITZAM_NULL_REF; 00214 ++p; 00215 } 00216 00217 return serial_data(refs,sizeof(itzam_ref) * size,true); 00218 } 00219 00221 00226 bool contains(const itzam_ref & ref) 00227 { 00228 return (m_refs.count(ref) > 0); 00229 } 00230 00232 00236 void add(const itzam_ref & ref) 00237 { 00238 if (!contains(ref)) 00239 m_refs.insert(ref); 00240 } 00241 00243 00247 void remove(const itzam_ref & ref) 00248 { 00249 if (contains(ref)) 00250 m_refs.erase(ref); 00251 } 00252 00254 00257 void remove_all() 00258 { 00259 m_refs.clear(); 00260 } 00261 00263 00267 size_t count() const 00268 { 00269 return m_refs.size(); 00270 } 00271 }; 00272 00274 00279 template <class key_t, class mutex_t = simple_mutex> 00280 class btree_index_base 00281 : public database_object, public mutex_t 00282 { 00283 public: 00285 typedef key_t key_type; 00286 00287 // internal data 00288 private: 00289 itzam_btree m_btree; 00290 00291 protected: 00293 00297 itzam_btree * get_btree() 00298 { 00299 return &m_btree; 00300 } 00301 00303 void lock() 00304 { 00305 mutex_t::lock(); 00306 } 00307 00309 void unlock() 00310 { 00311 mutex_t::unlock(); 00312 } 00313 00315 00322 btree_index_base(const string & name, itzam_file_lock_mode lock_mode) 00323 : database_object(name,lock_mode) 00324 { 00325 open(); 00326 } 00327 00329 00332 virtual ~btree_index_base() 00333 { 00334 close(); 00335 } 00336 00337 public: 00339 00345 virtual void open() 00346 { 00347 database_error error = DB_ERROR_NONE; 00348 00349 mutex_t::lock(); 00350 00351 switch (btree_index_base<key_t,mutex_t>::database_object::m_state) 00352 { 00353 case ITZAM_DB_OPEN: 00354 // ignore opening an open database 00355 break; 00356 00357 case ITZAM_DB_UNINIT: 00358 case ITZAM_DB_CLOSED: 00359 { 00360 // check for file existence 00361 struct stat dbstats; 00362 itzam_state state; 00363 00364 if (0 == stat(m_name.c_str(),&dbstats)) 00365 { 00366 state = itzam_btree_open(btree_index_base<key_t,mutex_t>::get_btree(), 00367 m_name.c_str(), 00368 m_file_lock_mode, 00369 key_t::compare_keys, 00370 false); 00371 } 00372 else 00373 { 00374 state = itzam_btree_create(btree_index_base<key_t,mutex_t>::get_btree(), 00375 m_name.c_str(), 00376 m_file_lock_mode, 00377 ITZAM_BTREE_ORDER_DEFAULT, 00378 key_t::compare_keys); 00379 } 00380 00381 if (state == ITZAM_OKAY) 00382 btree_index_base<key_t,mutex_t>::database_object::m_state = ITZAM_DB_OPEN; 00383 else 00384 { 00385 btree_index_base<key_t,mutex_t>::database_object::m_state = ITZAM_DB_BROKEN; 00386 error = DB_ERROR_OPEN_FAILED; 00387 } 00388 00389 break; 00390 } 00391 00392 default: 00393 error = DB_ERROR_OPEN_BROKEN; 00394 } 00395 00396 mutex_t::unlock(); 00397 00398 if (error != DB_ERROR_NONE) 00399 throw database_exception(error); 00400 } 00401 00403 00408 virtual void close() 00409 { 00410 database_error error = DB_ERROR_NONE; 00411 00412 mutex_t::lock(); 00413 00414 switch (btree_index_base<key_t,mutex_t>::database_object::m_state) 00415 { 00416 case ITZAM_DB_OPEN: 00417 { 00418 // close the file 00419 itzam_state state = itzam_btree_close(btree_index_base<key_t,mutex_t>::get_btree()); 00420 00421 if (state == ITZAM_OKAY) 00422 btree_index_base<key_t,mutex_t>::database_object::m_state = ITZAM_DB_CLOSED; 00423 else 00424 { 00425 btree_index_base<key_t,mutex_t>::database_object::m_state = ITZAM_DB_BROKEN; 00426 error = DB_ERROR_CLOSE_FAILED; 00427 } 00428 00429 break; 00430 } 00431 00432 case ITZAM_DB_CLOSED: 00433 // just ignore closing a closed database 00434 break; 00435 00436 default: 00437 error = DB_ERROR_CLOSE_BROKEN; 00438 } 00439 00440 mutex_t::unlock(); 00441 00442 if (error != DB_ERROR_NONE) 00443 throw database_exception(error); 00444 } 00445 00447 00452 virtual void insert(const key_t & key, itzam_ref ref) = 0; 00453 00455 00459 //virtual void remove(const key_t & key) = 0; 00460 00462 00467 virtual bool exists(const key_t & key) = 0; 00468 }; 00469 00471 00477 template <class key_t, class mutex_t = simple_mutex> 00478 class btree_index 00479 : public btree_index_base<key_t,mutex_t> 00480 { 00481 public: 00483 00490 btree_index(const string & name, itzam_file_lock_mode lock_mode) 00491 : btree_index_base<key_t,mutex_t>(name,lock_mode) 00492 { 00493 // everything handled by base class 00494 } 00495 00497 00500 ~btree_index() 00501 { 00502 // everything handled by base class 00503 } 00504 00506 00512 virtual void insert(const key_t & key, itzam_ref ref) 00513 { 00514 database_error error = DB_ERROR_NONE; 00515 00516 mutex_t::lock(); 00517 00518 switch (btree_index_base<key_t,mutex_t>::database_object::m_state) 00519 { 00520 case ITZAM_DB_OPEN: 00521 { 00522 serial_data raw_key = serial_data(key); 00523 00524 // prevent duplicate keys 00525 if (itzam_btree_find(btree_index_base<key_t,mutex_t>::get_btree(),raw_key.get_data()) != ITZAM_NULL_REF) 00526 error = DB_ERROR_WRITE_DUPE; 00527 else 00528 { 00529 if (ref == ITZAM_NULL_REF) 00530 error = DB_ERROR_WRITE_FAILED; 00531 else 00532 { 00533 // add key to the database 00534 itzam_state state = itzam_btree_insert(btree_index_base<key_t,mutex_t>::get_btree(), 00535 raw_key.get_data(), 00536 raw_key.get_size(), 00537 ref); 00538 00539 // handle an error 00540 if (state != ITZAM_OKAY) 00541 { 00542 btree_index_base<key_t,mutex_t>::database_object::m_state = ITZAM_DB_BROKEN; 00543 error = DB_ERROR_WRITE_FAILED; 00544 } 00545 } 00546 } 00547 00548 break; 00549 } 00550 case ITZAM_DB_CLOSED: 00551 error = DB_ERROR_WRITE_CLOSED; 00552 break; 00553 00554 default: 00555 error = DB_ERROR_WRITE_BROKEN; 00556 } 00557 00558 mutex_t::unlock(); 00559 00560 if (error != DB_ERROR_NONE) 00561 throw database_exception(error); 00562 } 00563 00565 00569 virtual itzam_ref remove(const key_t & key) 00570 { 00571 itzam_ref ref = ITZAM_NULL_REF; 00572 database_error error = DB_ERROR_NONE; 00573 00574 mutex_t::lock(); 00575 00576 switch (btree_index_base<key_t,mutex_t>::database_object::m_state) 00577 { 00578 case ITZAM_DB_OPEN: 00579 { 00580 // remove key 00581 itzam_state state = itzam_btree_remove(btree_index_base<key_t,mutex_t>::get_btree(), 00582 serial_data(key).get_data(), 00583 &ref); 00584 00585 // check for errors 00586 if (state != ITZAM_OKAY) 00587 error = DB_ERROR_REMOVE_FAILED; 00588 00589 break; 00590 } 00591 00592 case ITZAM_DB_CLOSED: 00593 error = DB_ERROR_REMOVE_CLOSED; 00594 break; 00595 00596 default: 00597 error = DB_ERROR_REMOVE_BROKEN; 00598 } 00599 00600 mutex_t::unlock(); 00601 00602 if (error != DB_ERROR_NONE) 00603 throw database_exception(error); 00604 00605 return ref; 00606 } 00607 00608 00610 00615 virtual bool exists(const key_t & key) 00616 { 00617 database_error error = DB_ERROR_NONE; 00618 00619 mutex_t::lock(); 00620 00621 bool result = false; 00622 00623 switch (btree_index_base<key_t,mutex_t>::database_object::m_state) 00624 { 00625 case ITZAM_DB_OPEN: 00626 // find data from key 00627 result = (ITZAM_NULL_REF != itzam_btree_find(btree_index_base<key_t,mutex_t>::get_btree(), 00628 serial_data(key).get_data())); 00629 break; 00630 00631 case ITZAM_DB_CLOSED: 00632 error = DB_ERROR_EXISTS_CLOSED; 00633 break; 00634 00635 default: 00636 error = DB_ERROR_EXISTS_BROKEN; 00637 } 00638 00639 mutex_t::unlock(); 00640 00641 if (error != DB_ERROR_NONE) 00642 throw database_exception(error); 00643 00644 return result; 00645 } 00646 00648 00653 virtual itzam_ref find(const key_t & key) 00654 { 00655 database_error error = DB_ERROR_NONE; 00656 00657 mutex_t::lock(); 00658 00659 itzam_ref ref = ITZAM_NULL_REF; 00660 00661 switch (btree_index_base<key_t,mutex_t>::database_object::m_state) 00662 { 00663 case ITZAM_DB_OPEN: 00664 // find data from key 00665 ref = itzam_btree_find(btree_index_base<key_t,mutex_t>::get_btree(), 00666 serial_data(key).get_data()); 00667 break; 00668 00669 case ITZAM_DB_CLOSED: 00670 error = DB_ERROR_EXISTS_CLOSED; 00671 break; 00672 00673 default: 00674 error = DB_ERROR_EXISTS_BROKEN; 00675 } 00676 00677 mutex_t::unlock(); 00678 00679 if (error != DB_ERROR_NONE) 00680 throw database_exception(error); 00681 00682 return ref; 00683 } 00684 }; 00685 00687 00693 template <class key_t, class mutex_t = simple_mutex> 00694 class btree_index_with_duplicates 00695 : public btree_index_base<key_t,mutex_t> 00696 { 00697 public: 00699 00706 btree_index_with_duplicates(const string & name, itzam_file_lock_mode lock_mode) 00707 : btree_index_base<key_t,mutex_t>(name,lock_mode) 00708 { 00709 // everything handled by base class 00710 } 00711 00713 00716 ~btree_index_with_duplicates() 00717 { 00718 // everything handled by base class 00719 } 00720 00722 00727 virtual void insert(const key_t & key, itzam_ref ref) 00728 { 00729 itzam_state state; 00730 database_error error = DB_ERROR_NONE; 00731 00732 mutex_t::lock(); 00733 00734 switch (btree_index_base<key_t,mutex_t>::database_object::m_state) 00735 { 00736 case ITZAM_DB_OPEN: 00737 { 00738 // get a pointer to the data file 00739 itzam_btree * btree = btree_index_base<key_t,mutex_t>::get_btree(); 00740 itzam_datafile * datafile = btree->m_datafile; 00741 00742 // create a key object 00743 serial_data raw_key = serial_data(key); 00744 00745 // get a reference for the key 00746 itzam_ref list_ref = itzam_btree_find(btree,raw_key.get_data()); 00747 00748 // did we find the key? 00749 if (list_ref == ITZAM_NULL_REF) 00750 { 00751 // key not found; we need to create a new reference list 00752 ref_list list; 00753 list.add(ref); 00754 00755 // write new list to file 00756 serial_data raw_list = serial_data(list); 00757 00758 itzam_ref list_ref = itzam_datafile_write(datafile, 00759 raw_list.get_data(), 00760 raw_list.get_size(), 00761 ITZAM_NULL_REF); 00762 00763 if (list_ref != ITZAM_NULL_REF) 00764 { 00765 // add key to the database 00766 state = itzam_btree_insert(btree, 00767 raw_key.get_data(), 00768 raw_key.get_size(), 00769 list_ref); 00770 00771 // handle an error 00772 if (state != ITZAM_OKAY) 00773 error = DB_ERROR_WRITE_FAILED; 00774 } 00775 else 00776 error = DB_ERROR_WRITE_FAILED; 00777 } 00778 else 00779 { 00780 // key found, we need to read reference to list 00781 void * raw_list_data; 00782 itzam_int raw_list_size; 00783 00784 // seek to record 00785 state = itzam_datafile_seek(datafile, list_ref); 00786 00787 if (state == ITZAM_OKAY) 00788 { 00789 // read raw record 00790 state = itzam_datafile_read_alloc(datafile, &raw_list_data, &raw_list_size); 00791 00792 if (state == ITZAM_OKAY) 00793 { 00794 // construct list object 00795 ref_list list(serial_data(raw_list_data,raw_list_size,true)); 00796 00797 // only add reference if it isn't in the list 00798 if (!list.contains(ref)) 00799 { 00800 // add reference 00801 list.add(ref); 00802 00803 // remove old key entry 00804 state = itzam_btree_remove(btree, 00805 raw_key.get_data(), 00806 &list_ref); 00807 00808 // remove old reference from the datafile 00809 state = itzam_datafile_seek(datafile, 00810 list_ref); 00811 00812 state = itzam_datafile_remove(datafile); 00813 00814 // convert updated list to serial object 00815 serial_data raw_list = serial_data(list); 00816 00817 // write updated table 00818 list_ref = itzam_datafile_write(datafile, 00819 raw_list.get_data(), 00820 raw_list.get_size(), 00821 ITZAM_NULL_REF); 00822 00823 // add key back in with new reference list 00824 state = itzam_btree_insert(btree, 00825 raw_key.get_data(), 00826 raw_key.get_size(), 00827 list_ref); 00828 } 00829 } 00830 else 00831 error = DB_ERROR_READ_FAILED; 00832 } 00833 else 00834 error = DB_ERROR_READ_FAILED; 00835 } 00836 00837 break; 00838 } 00839 case ITZAM_DB_CLOSED: 00840 error = DB_ERROR_WRITE_CLOSED; 00841 break; 00842 00843 default: 00844 error = DB_ERROR_WRITE_BROKEN; 00845 } 00846 00847 mutex_t::unlock(); 00848 00849 if (error != DB_ERROR_NONE) 00850 { 00851 btree_index_base<key_t,mutex_t>::database_object::m_state = ITZAM_DB_BROKEN; 00852 throw database_exception(error); 00853 } 00854 } 00855 00857 00861 virtual ref_list remove(const key_t & key) 00862 { 00863 ref_list result; 00864 00865 database_error error = DB_ERROR_NONE; 00866 00867 mutex_t::lock(); 00868 00869 switch (btree_index_base<key_t,mutex_t>::database_object::m_state) 00870 { 00871 case ITZAM_DB_OPEN: 00872 { 00873 // get a pointer to the data file 00874 itzam_btree * btree = btree_index_base<key_t,mutex_t>::get_btree(); 00875 itzam_datafile * datafile = btree->m_datafile; 00876 itzam_ref ref; 00877 00878 // remove key and (because of the NULL) data 00879 itzam_state state = itzam_btree_remove(btree, 00880 serial_data(key).get_data(), 00881 &ref); 00882 00883 // check for errors 00884 if (state == ITZAM_OKAY) 00885 { 00886 // read and return 00887 void * raw_list_data; 00888 itzam_int raw_list_size; 00889 00890 // seek to record 00891 state = itzam_datafile_seek(datafile, ref); 00892 00893 if (state == ITZAM_OKAY) 00894 { 00895 // read raw record 00896 state = itzam_datafile_read_alloc(datafile, &raw_list_data, &raw_list_size); 00897 00898 if (state == ITZAM_OKAY) 00899 result = ref_list(serial_data(raw_list_data,raw_list_size,true)); 00900 else 00901 error = DB_ERROR_REFLIST_MISSING; 00902 } 00903 else 00904 error = DB_ERROR_REFLIST_MISSING; 00905 } 00906 else 00907 error = DB_ERROR_REMOVE_FAILED; 00908 00909 break; 00910 } 00911 00912 case ITZAM_DB_CLOSED: 00913 error = DB_ERROR_REMOVE_CLOSED; 00914 break; 00915 00916 default: 00917 error = DB_ERROR_REMOVE_BROKEN; 00918 } 00919 00920 mutex_t::unlock(); 00921 00922 if (error != DB_ERROR_NONE) 00923 throw database_exception(error); 00924 00925 return result; 00926 } 00927 00929 00934 virtual void remove_ref(const key_t & key, itzam_ref ref) 00935 { 00936 itzam_state state; 00937 database_error error = DB_ERROR_NONE; 00938 00939 mutex_t::lock(); 00940 00941 switch (btree_index_base<key_t,mutex_t>::database_object::m_state) 00942 { 00943 case ITZAM_DB_OPEN: 00944 { 00945 // get a pointer to the data file 00946 itzam_btree * btree = btree_index_base<key_t,mutex_t>::get_btree(); 00947 itzam_datafile * datafile = btree->m_datafile; 00948 00949 // create a key object 00950 serial_data raw_key = serial_data(key); 00951 00952 // get a reference for the key 00953 itzam_ref list_ref = itzam_btree_find(btree,raw_key.get_data()); 00954 00955 // did we find the list? 00956 if (list_ref != ITZAM_NULL_REF) 00957 { 00958 state = itzam_datafile_seek(datafile,list_ref); 00959 00960 if (state == ITZAM_OKAY) 00961 { 00962 // need to read reference to list 00963 void * raw_list_data; 00964 itzam_int raw_list_size; 00965 00966 // read raw record 00967 state = itzam_datafile_read_alloc(datafile, &raw_list_data, &raw_list_size); 00968 00969 if (state == ITZAM_OKAY) 00970 { 00971 // construct list object 00972 ref_list list(serial_data(raw_list_data,raw_list_size,true)); 00973 00974 // only remove reference if it is in the list 00975 if (list.contains(ref)) 00976 { 00977 // add reference 00978 list.remove(ref); 00979 00980 // remove old key entry 00981 state = itzam_btree_remove(btree, 00982 raw_key.get_data(), 00983 &list_ref); 00984 00985 if (state == ITZAM_OKAY) 00986 { 00987 // remove old reference from the datafile 00988 state = itzam_datafile_seek(datafile, 00989 list_ref); 00990 00991 if (state == ITZAM_OKAY) 00992 { 00993 state = itzam_datafile_remove(datafile); 00994 00995 if (state == ITZAM_OKAY) 00996 { 00997 // convert updated list to serial object 00998 serial_data raw_list = serial_data(list); 00999 01000 // write updated table 01001 list_ref = itzam_datafile_write(datafile, 01002 raw_list.get_data(), 01003 raw_list.get_size(), 01004 ITZAM_NULL_REF); 01005 01006 if (state == ITZAM_OKAY) 01007 { 01008 // add key back in with new reference list 01009 state = itzam_btree_insert(btree, 01010 raw_key.get_data(), 01011 raw_key.get_size(), 01012 list_ref); 01013 } 01014 } 01015 } 01016 } 01017 } 01018 } 01019 01020 if (state != ITZAM_OKAY) 01021 error = DB_ERROR_READ_FAILED; 01022 } 01023 else 01024 error = DB_ERROR_READ_FAILED; 01025 } 01026 break; 01027 } 01028 01029 case ITZAM_DB_CLOSED: 01030 error = DB_ERROR_REMOVE_CLOSED; 01031 break; 01032 01033 default: 01034 error = DB_ERROR_REMOVE_BROKEN; 01035 } 01036 01037 mutex_t::unlock(); 01038 01039 if (error != DB_ERROR_NONE) 01040 throw database_exception(error); 01041 } 01042 01043 01045 01050 virtual bool exists(const key_t & key) 01051 { 01052 database_error error = DB_ERROR_NONE; 01053 01054 mutex_t::lock(); 01055 01056 bool result = false; 01057 01058 switch (btree_index_base<key_t,mutex_t>::database_object::m_state) 01059 { 01060 case ITZAM_DB_OPEN: 01061 // find data from key 01062 result = (ITZAM_NULL_REF != itzam_btree_find(btree_index_base<key_t,mutex_t>::get_btree(), 01063 serial_data(key).get_data())); 01064 break; 01065 01066 case ITZAM_DB_CLOSED: 01067 error = DB_ERROR_EXISTS_CLOSED; 01068 break; 01069 01070 default: 01071 error = DB_ERROR_EXISTS_BROKEN; 01072 } 01073 01074 mutex_t::unlock(); 01075 01076 if (error != DB_ERROR_NONE) 01077 throw database_exception(error); 01078 01079 return result; 01080 } 01081 01083 01088 virtual ref_list find(const key_t & key) 01089 { 01090 ref_list result; 01091 itzam_state state; 01092 database_error error = DB_ERROR_NONE; 01093 01094 mutex_t::lock(); 01095 01096 itzam_ref ref = ITZAM_NULL_REF; 01097 01098 switch (btree_index_base<key_t,mutex_t>::database_object::m_state) 01099 { 01100 case ITZAM_DB_OPEN: 01101 { 01102 // get a pointer to the data file 01103 itzam_btree * btree = btree_index_base<key_t,mutex_t>::get_btree(); 01104 itzam_datafile * datafile = btree->m_datafile; 01105 01106 // find data from key 01107 itzam_ref ref = itzam_btree_find(btree, serial_data(key).get_data()); 01108 01109 if (ref != ITZAM_NULL_REF) 01110 { 01111 state = itzam_datafile_seek(datafile, ref); 01112 01113 if (state == ITZAM_OKAY) 01114 { 01115 // read and return 01116 void * raw_list_data; 01117 itzam_int raw_list_size; 01118 01119 // read raw record 01120 state = itzam_datafile_read_alloc(btree_index_base<key_t,mutex_t>::get_btree()->m_datafile, 01121 &raw_list_data, 01122 &raw_list_size); 01123 01124 if (state == ITZAM_OKAY) 01125 result = ref_list(serial_data(raw_list_data,raw_list_size,true)); 01126 else 01127 error = DB_ERROR_REFLIST_MISSING; 01128 } 01129 else 01130 error = DB_ERROR_REFLIST_MISSING; 01131 } 01132 01133 break; 01134 } 01135 case ITZAM_DB_CLOSED: 01136 error = DB_ERROR_EXISTS_CLOSED; 01137 break; 01138 01139 default: 01140 error = DB_ERROR_EXISTS_BROKEN; 01141 } 01142 01143 mutex_t::unlock(); 01144 01145 if (error != DB_ERROR_NONE) 01146 throw database_exception(error); 01147 01148 return result; 01149 } 01150 }; 01151 01153 01158 template <class mutex_t = simple_mutex> 01159 class matrix_index 01160 : public database_object, public mutex_t 01161 { 01162 // internal data 01163 private: 01164 itzam_int m_cols; 01165 itzam_int m_rows; 01166 itzam_matrix m_matrix; 01167 01168 public: 01170 01179 matrix_index(const string & name, itzam_file_lock_mode lock_mode, itzam_int cols, itzam_int rows) 01180 : database_object(name, lock_mode), 01181 m_cols(cols), 01182 m_rows(rows) 01183 { 01184 open(); 01185 } 01186 01188 01191 virtual ~matrix_index() 01192 { 01193 close(); 01194 } 01195 01197 01203 void open() 01204 { 01205 database_error error = DB_ERROR_NONE; 01206 01207 mutex_t::lock(); 01208 01209 switch (matrix_index<mutex_t>::database_object::m_state) 01210 { 01211 case ITZAM_DB_OPEN: 01212 // ignore opening an open file 01213 break; 01214 01215 case ITZAM_DB_UNINIT: 01216 case ITZAM_DB_CLOSED: 01217 { 01218 // check for file existence 01219 struct stat dbstats; 01220 itzam_state state; 01221 01222 if (0 == stat(m_name.c_str(),&dbstats)) 01223 { 01224 state = itzam_matrix_open(&m_matrix, 01225 m_name.c_str(), 01226 m_file_lock_mode, 01227 false); 01228 } 01229 else 01230 { 01231 state = itzam_matrix_create(&m_matrix, 01232 m_cols, 01233 m_rows, 01234 m_name.c_str(), 01235 m_file_lock_mode); 01236 } 01237 01238 if (state == ITZAM_OKAY) 01239 matrix_index<mutex_t>::database_object::m_state = ITZAM_DB_OPEN; 01240 else 01241 { 01242 matrix_index<mutex_t>::database_object::m_state = ITZAM_DB_BROKEN; 01243 error = DB_ERROR_OPEN_FAILED; 01244 } 01245 01246 break; 01247 } 01248 01249 default: 01250 error = DB_ERROR_OPEN_BROKEN; 01251 } 01252 01253 mutex_t::unlock(); 01254 01255 if (error != DB_ERROR_NONE) 01256 throw database_exception(error); 01257 } 01258 01260 01265 void close() 01266 { 01267 database_error error = DB_ERROR_NONE; 01268 01269 mutex_t::lock(); 01270 01271 switch (matrix_index<mutex_t>::database_object::m_state) 01272 { 01273 case ITZAM_DB_OPEN: 01274 { 01275 // close the file 01276 itzam_state state = itzam_matrix_close(&m_matrix); 01277 01278 if (state == ITZAM_OKAY) 01279 matrix_index<mutex_t>::database_object::m_state = ITZAM_DB_CLOSED; 01280 else 01281 { 01282 matrix_index<mutex_t>::database_object::m_state = ITZAM_DB_BROKEN; 01283 error = DB_ERROR_CLOSE_FAILED; 01284 } 01285 01286 break; 01287 } 01288 01289 case ITZAM_DB_CLOSED: 01290 // ignore closing a closed file 01291 break; 01292 01293 default: 01294 error = DB_ERROR_CLOSE_BROKEN; 01295 } 01296 01297 mutex_t::unlock(); 01298 01299 if (error != DB_ERROR_NONE) 01300 throw database_exception(error); 01301 } 01302 01304 01310 void insert(itzam_int col, itzam_int row, itzam_ref ref) 01311 { 01312 database_error error = DB_ERROR_NONE; 01313 01314 mutex_t::lock(); 01315 01316 switch (database_object::m_state) 01317 { 01318 case ITZAM_DB_OPEN: 01319 { 01320 // prevent duplicate keys 01321 if (itzam_matrix_find(&m_matrix,col,row) != ITZAM_NULL_REF) 01322 error = DB_ERROR_WRITE_DUPE; 01323 else 01324 { 01325 if (ref == ITZAM_NULL_REF) 01326 error = DB_ERROR_WRITE_FAILED; 01327 else 01328 { 01329 // add key to the database 01330 itzam_state state = itzam_matrix_insert(&m_matrix,col,row,ref); 01331 01332 // handle an error 01333 if (state != ITZAM_OKAY) 01334 { 01335 database_object::m_state = ITZAM_DB_BROKEN; 01336 error = DB_ERROR_WRITE_FAILED; 01337 } 01338 } 01339 } 01340 01341 break; 01342 } 01343 case ITZAM_DB_CLOSED: 01344 error = DB_ERROR_WRITE_CLOSED; 01345 break; 01346 01347 default: 01348 error = DB_ERROR_WRITE_BROKEN; 01349 } 01350 01351 mutex_t::unlock(); 01352 01353 if (error != DB_ERROR_NONE) 01354 throw database_exception(error); 01355 } 01356 01358 01364 virtual itzam_ref remove(itzam_int col, itzam_int row) 01365 { 01366 itzam_ref ref = ITZAM_NULL_REF; 01367 database_error error = DB_ERROR_NONE; 01368 01369 mutex_t::lock(); 01370 01371 switch (database_object::m_state) 01372 { 01373 case ITZAM_DB_OPEN: 01374 { 01375 // remove key 01376 itzam_state state = itzam_matrix_remove(&m_matrix,col,row,&ref); 01377 01378 // check for errors 01379 if (state != ITZAM_OKAY) 01380 error = DB_ERROR_REMOVE_FAILED; 01381 01382 break; 01383 } 01384 01385 case ITZAM_DB_CLOSED: 01386 error = DB_ERROR_REMOVE_CLOSED; 01387 break; 01388 01389 default: 01390 error = DB_ERROR_REMOVE_BROKEN; 01391 } 01392 01393 mutex_t::unlock(); 01394 01395 if (error != DB_ERROR_NONE) 01396 throw database_exception(error); 01397 01398 return ref; 01399 } 01400 01402 01408 bool exists(itzam_int col, itzam_int row) 01409 { 01410 database_error error = DB_ERROR_NONE; 01411 01412 mutex_t::lock(); 01413 01414 bool result = false; 01415 01416 switch (database_object::m_state) 01417 { 01418 case ITZAM_DB_OPEN: 01419 // find data from key 01420 result = (ITZAM_NULL_REF != itzam_matrix_find(&m_matrix,col,row)); 01421 break; 01422 01423 case ITZAM_DB_CLOSED: 01424 error = DB_ERROR_EXISTS_CLOSED; 01425 break; 01426 01427 default: 01428 error = DB_ERROR_EXISTS_BROKEN; 01429 } 01430 01431 mutex_t::unlock(); 01432 01433 if (error != DB_ERROR_NONE) 01434 throw database_exception(error); 01435 01436 return result; 01437 } 01438 01440 01446 itzam_ref find(itzam_int col, itzam_int row) 01447 { 01448 database_error error = DB_ERROR_NONE; 01449 01450 mutex_t::lock(); 01451 01452 itzam_ref ref = ITZAM_NULL_REF; 01453 01454 switch (database_object::m_state) 01455 { 01456 case ITZAM_DB_OPEN: 01457 // find data from key 01458 ref = itzam_matrix_find(&m_matrix,col,row); 01459 break; 01460 01461 case ITZAM_DB_CLOSED: 01462 error = DB_ERROR_EXISTS_CLOSED; 01463 break; 01464 01465 default: 01466 error = DB_ERROR_EXISTS_BROKEN; 01467 } 01468 01469 mutex_t::unlock(); 01470 01471 if (error != DB_ERROR_NONE) 01472 throw database_exception(error); 01473 01474 return ref; 01475 } 01476 }; 01477 01479 01490 template < class record_t, class mutex_t = simple_mutex > 01491 class database_file 01492 : public database_object, public mutex_t 01493 { 01494 // type definitions 01495 public: 01497 typedef record_t record_type; 01498 01499 // internal data 01500 protected: 01502 itzam_datafile m_datafile; 01503 01505 void lock() 01506 { 01507 mutex_t::lock(); 01508 } 01509 01511 void unlock() 01512 { 01513 mutex_t::unlock(); 01514 } 01515 01516 // interface 01517 public: 01519 01527 database_file(const string & name, itzam_file_lock_mode lock_mode) 01528 : database_object(name, lock_mode) 01529 { 01530 open(); 01531 } 01532 01534 01537 virtual ~database_file() 01538 { 01539 close(); 01540 } 01541 01543 01549 virtual void open() 01550 { 01551 database_error error = DB_ERROR_NONE; 01552 01553 mutex_t::lock(); 01554 01555 switch (database_object::m_state) 01556 { 01557 case ITZAM_DB_OPEN: 01558 // ignore opening an open database 01559 break; 01560 01561 case ITZAM_DB_UNINIT: 01562 case ITZAM_DB_CLOSED: 01563 { 01564 // check for file existence 01565 struct stat dbstats; 01566 itzam_state state; 01567 01568 if (0 == stat(m_name.c_str(),&dbstats)) 01569 state = itzam_datafile_open(&m_datafile,m_name.c_str(),m_file_lock_mode,false); 01570 else 01571 state = itzam_datafile_create(&m_datafile,m_name.c_str(),m_file_lock_mode); 01572 01573 if (state == ITZAM_OKAY) 01574 database_object::m_state = ITZAM_DB_OPEN; 01575 else 01576 { 01577 database_object::m_state = ITZAM_DB_BROKEN; 01578 error = DB_ERROR_OPEN_FAILED; 01579 } 01580 01581 break; 01582 } 01583 01584 default: 01585 mutex_t::unlock(); 01586 throw database_exception(DB_ERROR_OPEN_BROKEN); 01587 } 01588 01589 mutex_t::unlock(); 01590 01591 if (error != DB_ERROR_NONE) 01592 throw database_exception(error); 01593 } 01594 01596 01601 virtual void close() 01602 { 01603 database_error error = DB_ERROR_NONE; 01604 01605 mutex_t::lock(); 01606 01607 switch (database_object::m_state) 01608 { 01609 case ITZAM_DB_OPEN: 01610 { 01611 // close the file 01612 itzam_state state = itzam_datafile_close(&m_datafile); 01613 01614 if (state == ITZAM_OKAY) 01615 database_object::m_state = ITZAM_DB_CLOSED; 01616 else 01617 { 01618 database_object::m_state = ITZAM_DB_BROKEN; 01619 error = DB_ERROR_CLOSE_FAILED; 01620 } 01621 01622 break; 01623 } 01624 01625 case