📄 readme.htm
字号:
C pointers. POST++ storage provides two methods allowing you to specify and
obtain reference to the root object:
<PRE>
void set_root_object(object* obj);
object* get_root_object();
</PRE>
When you create new storage <CODE>get_root_object()</CODE> returns NULL.
You should create root object and store reference to it by
<CODE>set_root_object()</CODE> method. Next time you are opening storage,
root object can be retrieved by <CODE>get_root_object()</CODE>.<P>
<B><I>Hint</I></B>: In practice application classes used to be changed during
program development and support. Unfortunately POST++ due to its simplicity
provides no facilities for automatic object conversion (see for example
lazy object update scheme in
<A HREF = "http://www.ispras.ru/~knizhnik/goods.html">GOODS</A>),
So to avoid problems with adding new fields to the objects, I can recommend
you to reserve some free space in objects for future use. This is especially
significant for root object, because it is first candidate for adding new
components. You should also avoid reverse references to the root object.
If no other object has reference to the root objects, then root object
can be simply changed (by means of <CODE>set_root_object</CODE> method)
to instance of new class. POST++ storage provides methods for setting
and retrieving storage version identifier. This identifier can be used
by application for updating objects in the storage depending on the storage
and the application versions.<P>
<H2><A NAME = "storage::new">Storage constructor</A></H2>
You can use several storages in your application simultaneously.
Storage constructor takes one mandatory argument - path to the storage file.
If this file has no extension, then POST appends suffix ".odb" to the name
of the file.
This file name is also used by POST++ to form names of some auxiliary files:<P>
<TABLE>
<TR><TH>file description</TH><TH>when used</TH><TH>suffix</TH></TR>
<TR><TD>temporary file with new storage image</TD>
<TD>used in non-transaction mode to store new image of storage</TD>
<TD>".tmp"</TD></TR>
<TR><TD>transaction log file</TD>
<TD>used in transaction mode to saved shadow pages</TD>
<TD>".log"</TD></TR>
<TR><TD>saved copy of storage file</TD>
<TD>used only in Windows-95 for renaming temporary file</TD>
<TD>".sav"</TD></TR></TABLE><P>
Two other parameters of storage constructor have default values.
First of them <CODE>max_file_size</CODE> specifies limitation of
storage file extension. If storage file is larger than
<CODE>storage::max_file_size</CODE> then it will not be truncated but
further extends are not possible. If <CODE>max_file_size</CODE> is
greater than the file size, then behavior depends on storage opening mode.
In transaction mode, file is mapped on memory with read-write protection.
Windows-NT/95 extends in this case size of the file till
<CODE>max_file_size</CODE>. The file size will be truncated by
<CODE>storage::close()</CODE> method to the boundary of last object allocated
in the storage. In Windows it is necessary to have at least
<CODE>storage::max_file_size</CODE> free bytes on disk to successfully
open storage in read-write mode even if you are not going to add new objects
in the storage.<P>
The last parameter of storage constructor is <CODE>max_locked_objects</CODE>,
This parameter is used only in transaction mode to provide buffering
of shadow pages writes to the transaction log file. To provide
data consistency POST++ should guarantee that shadow page will be
saved in the transaction log file before modified page will
be flushed on disk. POST++ use one of two approaches:
synchronous log writes (<CODE>max_locked_objects == 0</CODE>)
and buffered writes with locking of pages in memory. By locking
page in the memory, we can guaranty that it will not be swapped out
on disk before transaction log buffers. Shadow pages are written to
the transaction log file in asynchronous mode (with operating system
cashing enabled). When number of locked pages exceeds
<CODE>max_locked_pages</CODE>, log file buffers are flushed on disk
and all locked pages are unlocked. Such approach can significantly
increase transaction performance (up to 5 times under NT). But unfortunately
different operating systems use different approaches to locking pages in
memory.
<UL>
<LI> Windows 95 doesn't support it at all.
<LI> In Windows NT every process can lock it's pages, but total number of
locked pages should not exceed process working set quota. By default
process can't lock more than 30 pages. If you specify
<CODE>max_locked_pages</CODE> parameter greater than 30, than
POST++ will try to extend process working set to feet your
requirement. But my experiments show that difference in performance
with 30 and 60 locked pages is very negligible.
<LI> In Unix only superuser can lock pages in memory. That is why
file constructor checks if process has enough permissions
to use lock operations. So if you specify <CODE>max_locked_pages</CODE>
parameter greater than 0, then decision whether to use synchronous or
asynchronous writes to the transaction log file will be taken at
moment of storage class creation. If you want to use benefits of
memory locking mechanism (2-5 times, depending on type of transaction),
you should change owner of your application to <CODE>root</CODE> and
grant <CODE> set-user-ID</CODE> permission:
<CODE>chmod +s application</CODE>.
</UL><P>
<H2><A NAME = "storage::open">Opening storage</A></H2>
POST++ uses memory mapping mechanism for accessing data from
the file. Two different approaches are used in POST++ to provide
storage data consistency. First and more advanced is based on
transaction mechanism using shadow pages to provide storage recovery
after fault and transaction rollback. <I>Before write shadow page creation</I>
algorithm is used. This algorithm is implemented in the following way:
all mapped on file pages are set to readonly protection. Any write
access to such page will cause access violation exception. This exception
is handled by special handler, which change page protection to
read-write and place copy of this page in transaction log file (log file name
is combined from the original data file name and suffix ".log").
All following write accesses to this page will not cause page faults.
Storage method <CODE>commit()</CODE> flushes all modified pages on disk and
truncates the log file. <CODE>storage::commit()</CODE> method is implicitly
called by <CODE>storage::close()</CODE>. If fault happened before
<CODE>storage::commit()</CODE> operation, all changes will be undone by coping
modified pages from transaction log to the storage data file. Also all changes
can be undone explicitly by <CODE>storage::rollback()</CODE> method. To choose
transaction based model of data file access, specify
<CODE>storage::use_transaction_log</CODE> attribute for
<CODE>storage::open()</CODE> method.<P>
Another approach to providing data consistency is based on
copy on write mechanism. In this case original file is not affected.
Any attempt to modify page that is mapped on the file, cause creation
copy of the page, which is allocated from system swap and has read-write
access. File is updated only by explicit call of <CODE>storage::flush()</CODE>
method. This method writes data to temporary file (with suffix ".tmp")
and then renames this file to original one.
So this operation cause an atomic update of the file (certainly if
operating system can guaranty atomicity of <CODE>rename()</CODE> operation).<P>
<B><I>Attention:</I></B> If you are not using transactions,
<CODE>storage::close()</CODE> method doesn't flush data in the file. So if
you don't call <CODE>storage::flush()</CODE> method before
<CODE>storage::close()</CODE> all modifications done since last
<CODE>flush</CODE> will be lost.<P>
<B><I>Windows 95 specific:</I></B> In Windows 95
rename to existing file is not possible, so
original file is first saved in file with name with suffix ".sav".
Then temporary file wit suffix ".tmp" is renamed
to the original name and finally old copy is removed. So if fault
is happened during <CODE>flush()</CODE> operation and after it you find
no storage file, please do not panic, just look for file with name terminated
with ".sav" suffix and rename it to the original one.<P>
<B><I>Hint:</I></B> I recommend you to use transactions if you are planning
to save data during program execution. It is also possible with
copy on write approach but it is much more expensive. Also transactions are
always preferable if size of storage is large, because creating temporary
copy of file will require a lot of disk space and time.<P>
There are several attributes, which can be passed to storage
<CODE>open()</CODE> method:
<DL>
<DT><B>support_virtual_functions</B><DD>
This attribute should be set if objects
with virtual functions are placed in the storage. If this attribute is not set,
POST++ decides that all persistent objects contain references only within
storage (to other objects in the storage). So adjustment of references
should be done only if base address of data file mapping is changed
(this address is stored in the first word of data file and POST++ always
tries to map file to the same address to avoid unnecessary reference
adjustment). But if object class contains virtual functions, pointer to
virtual table is placed inside object. If you recompile your application,
address of this table can be changed. POST++ library compares timestamp
of executable image with timestamp stored in database created by this
application. If these timestamp are not equal, correction of virtual table
pointer should be performed. To get application timestamp POST++
should locate executable file image. Unfortunately there is no portable
way to find out executable file name in Unix. Under Unix POST++ looks
at the value of environment variable "_", which is set by shell.
This approach will not work if process was started not by shell
(for example by <CODE>system()</CODE>) or working directory is changed
by <CODE>chdir()</CODE>. The most portable way is to use file
<A HREF = "comptime.cxx">comptime.cxx</A>, which should be compiled each
time you recompile your application and linked together with storage
library. There is no such problem in Windows, where name of executable image
can be obtained by Win32 API. While storage
opening POST++ compares this timestamp with timestamp stored in the data file
and if they are different and <CODE>support_virtual_functions</CODE> attribute
is specified then correction of all objects (by calling default constructor)
will be done.
<DT><B>read_only</B><DD>
By setting this attribute programmer says that he wants only readonly access
to the data file. POST++ will create readonly view of the data file and any
attempt to change some object in the storage or allocate new one will cause
protection violation fault. There is one exception: if it is impossible to map
data file to the same address or application is changed and
<CODE>support_virtual_functions</CODE> is specified, then protection of region
is temporary changed to copy on write and conversion of loaded objects takes
place.
<DT><B>use_transaction_log</B><DD>
Setting of this attribute force using transactions for all data file
updates. Shadow page strategy is used for implementing transactions.
Transaction is opened implicitly when storage first modification
of storage is done. It is closed explicitly either by
<CODE>storage::commit()</CODE> or by <CODE>storage::rollback()</CODE>
operations. Method <CODE>storage::commit()</CODE> saves all modified
pages on disk and truncates transaction log, method
<CODE>storage::rollback()</CODE> undo all changes made within this transaction.
<DT><B>no_file_mapping</B><DD>
By default POST++ will map data file to the process virtual memory.
Time of opening database is greatly reduced in this case, because pages
of the file will be loaded on demand. But if size of database is not so large
or all data from the database need to be accessed immediately, then
reading file to memory can be preferable to using virtual memory mapping
because no extra overhead of handling page faults takes place in this case.
Flag <code>no_file_mapping</code> prevents POST++ from mapping file
and cause reading it in allocated memory segment.
<DT><B>fault_tolerant</B><DD>
This flag should be used by applications which want to preserve database
consistency in case of system or application fault. It is not necessary to
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -