📄 fastdb.htm
字号:
detect type of passed argument and union with argument value.
The following table contains mapping between argument types and where the value should be taken from:<P>
<TABLE BORDER ALIGN=CENTER>
<TR><TH>Argument type</TH><TH>Argument value</TH><TH>Argument value type</TH></TR>
<TR><TD><code>dbUserFunctionArgument::atInteger</code></TD><TD><code>u.intValue</code></TD><TD><code>int8</code></TD></TR>
<TR><TD><code>dbUserFunctionArgument::atBoolean</code></TD><TD><code>u.boolValue</code></TD><TD><code>bool</code></TD></TR>
<TR><TD><code>dbUserFunctionArgument::atString</code></TD><TD><code>u.strValue</code></TD><TD><code>char const*</code></TD></TR>
<TR><TD><code>dbUserFunctionArgument::atReal</code></TD><TD><code>u.realValue</code></TD><TD><code>real8</code></TD></TR>
<TR><TD><code>dbUserFunctionArgument::atReference</code></TD><TD><code>u.oidValue</code></TD><TD><code>oid_t</code></TD></TR>
<TR><TD><code>dbUserFunctionArgument::atRawBinary</code></TD><TD><code>u.rawValue</code></TD><TD><code>void*</code></TD></TR>
</TABLE><P>
For example the following
statements make it possible to use the <code>sin</code> function in SQL
statements:
<PRE>
#include <math.h>
...
USER_FUNC(sin);
</PRE>
Functions can be used only
within the application, where they are defined. Functions are not accessible
from other applications and interactive SQL. If a function returns a string
type , the returned string should be copied by means of the operator
<code>new</code>, because
FastDB will call the destructor after copying the returned value.<P>
In FastDB, the function argument can (but not necessarily must) be enclosed in
parentheses. So both of the following expressions are valid:
<PRE>
'$' + string(abs(x))
length string y
</PRE><P>
Functions with two argument can be also used as operators. Consider the following example,
in which function <code>contains</code> which performs case insensitive search for substring is defined:
<PRE>
bool contains(dbUserFunctionArgument& arg1, dbUserFunctionArgument& arg2) {
assert(arg1.type == dbUserFunctionArgument::atString
&& arg2.type == dbUserFunctionArgument::atString);
return stristr(arg1.u.strValue, arg2.u.strValue) != NULL;
}
USER_FUNC(contains);
dbQuery q1, q2;
q1 = "select * from TestTable where name contains 'xyz'";
q2 = "select * from TestTable where contains(name, 'xyz')";
</PRE>
In this example, queries <code>q1</code> and <code>q2</code> are equivalent.<P>
<H2><A NAME = "cpp">C++ interface</A></H2>
One of the primary goals of FastDB is to provide a flexible and convenient
application language interface. Anyone who has to use
ODBC or similar SQL interfaces will understand what I am speaking about.
In FastDB, a query can be written in C++ in the following way:<P>
<PRE>
dbQuery q;
dbCursor<Contract> contracts;
dbCursor<Supplier> suppliers;
int price, quantity;
q = "(price >=",price,"or quantity >=",quantity,
") and delivery.year=1999";
// input price and quantity values
if (contracts.select(q) != 0) {
do {
printf("%s\n", suppliers.at(contracts->supplier)->company);
} while (contracts.next());
}
</PRE>
<H3><A NAME = "table">Table</A></H3>
Data in FastDB is stored in tables which correspond to C++ classes
whereas the table records correspond to class instances.
The following C++ types are accepted as atomic components of
FastDB records:<P>
<TABLE BORDER ALIGN="center">
<TR><TH>Type</TH><TH>Description</TH></TR>
<TR><TD>bool</TD><TD>boolean type (<code>true,false</code>)</TD></TR>
<TR><TD>int1</TD><TD>one byte signed integer (-128..127)</TD></TR>
<TR><TD>int2</TD><TD>two bytes signed integer (-32768..32767)</TD></TR>
<TR><TD>int4</TD><TD>four bytes signed integer (-2147483648..2147483647)</TD></TR>
<TR><TD>int8</TD><TD>eight bytes signed integer (-2**63..2**63-1)</TD></TR>
<TR><TD>real4</TD><TD>four bytes ANSI floating point type</TD></TR>
<TR><TD>real8</TD><TD>eight bytes ANSI double precision floating point type</TD></TR>
<TR><TD>char const*</TD><TD>zero terminated string</TD></TR>
<TR><TD>dbReference<T></TD><TD>reference to class T</TD></TR>
<TR><TD>dbArray<T></TD><TD>dynamic array of elements of type T</TD></TR>
</TABLE><P>
In addition to types specified in the table above, FastDB records can
also contain nested structures of these components.
FastDB doesn't support unsigned types to simplify the query language,
to eliminate bugs caused by signed/unsigned comparison
and to reduce the size of the database engine.<P>
Unfortunately C++ provides no way
to get metainformation about a class at runtime (RTTI is not supported by all
compilers and also doesn't provide enough information).
Therefore the programmer
has to explicitly enumerate class fields to be included in the database table
(it also makes mapping between classes and tables more flexible).
FastDB provides a set of macros and classes to make such mapping as simple as
possible.<P>
Each C++ class or structure, which will be used in the database, should
contain a special method describing its fields. The macro
<code>TYPE_DESCRIPTOR(</code><I>field_list</I><code>)</code> will construct
this method. The single argument of this macro is - enclosed in parentheses -
a list of class field descriptors.
If you want to define some methods for the class
and make them available for the database, then the macro
<code>CLASS_DESCRIPTOR(</code><I>name, field_list</I><code>)</code>
should be used instead of <code>TYPE_DESCRIPTOR</code>. The class name is
needed to get references to member functions.<P>
The following macros can be used for the construction of field
descriptors:
<DL>
<DT><B>FIELD(</B>name<B>)</B><DD>Non-indexed field with specified name.
<DT><B>KEY(</B>name, index_type<B>)</B><DD>Indexed field. <I>index_type</I>
should be a combination of <code>HASHED</code> and <code>INDEXED</code> flags.
When the <code>HASHED</code> flag is specified, FastDB will create a hash table
for the table using this field as a key. When the <code>INDEXED</code> flag is
specified, FastDB will create a (special kind of index) T-tree for the table
using this field as a key.
<DT><B>UDT(</B>name, index_type, comparator<B>)</B><DD>User defined raw binary type.
Database deals with this type just as with sequence of bytes of specified size.
This field can be used in query (compared with query parameter of the same type),
may be indexed and used in <code>order by</code> clause. Comparison is performed by means of
<code>comparator</code> function provided by programmer. Comparator functions receives three
arguments: two pointers to the compared raw binary objects and size of binary object.
The semantic of <I>index_type</I> is the same as of <code>KEY</code> macro.
<DT><B>RAWKEY(</B>name, index<B>)</B><DD>Raw binary type with predefined comparator.
This macro is just specialized version of <code>UDT</code> macro with <code>memcmp</code>
used as comparator.
<DT><B>RAWFIELD(</B>name<B>)</B><DD>One more specialization of <code>UDT</code> macro
for raw binary fields with predefined comparator <code>memcmp</code> and without indices.
<DT><B>SUPERCLASS(</B>name<B>)</B><DD>Specifies information about the base class
(parent) of the current class.
<DT><B>RELATION(</B>reference, inverse_reference<B>)</B>
<DD>Specifies <I>one-to-one, one-to-many</I> or <I>many-to-many</I>
relationships between classes (tables). Both <I>reference</I>
and <I>inverse_reference</I>
fields should be of reference or of array of reference type.
<code>inverse_reference</code> is a field of the referenced table
containing the inverse reference(s) to the current table. Inverse references
are automatically updated by FastDB and are used for query optimization
(see <A HREF="#inverse">Inverse references</A>).
<DT><B>OWNER(</B>reference, inverse_reference<B>)</B>
<DD>Specifies <I>one-to-many</I> or <I>many-to-many</I>
relationship between classes (tables) of owner-member type.
When owner record is removed all referenced member records are also removed
(cascade delete). If member record has reference to owner class, it should be
declared with RELATION macro.
<DT><B>METHOD(</B>name<B>)</B><DD>Specifies a method of the class.
The method should be a parameterless instance member function
returning a
boolean, numeric, reference or string type. Methods should be specified after
all other attributes of the class.
</DL><P>
Although only atomic fields can be indexed, an index type can be specified
for structures. The index will be created for components of the structure
only if such type of index is specified in the index type mask of the
structure. This allows the programmers to enable or disable indices for
structure fields depending on the role of the structure in the record.<P>
The following example illustrates the creation of a type descriptor
in the header file:<P>
<PRE>
class dbDateTime {
int4 stamp;
public:
int year() {
return localtime((time_t*)&stamp)->tm_year + 1900;
}
...
CLASS_DESCRIPTOR(dbDateTime,
(KEY(stamp,INDEXED|HASHED),
METHOD(year), METHOD(month), METHOD(day),
METHOD(dayOfYear), METHOD(dayOfWeek),
METHOD(hour), METHOD(minute), METHOD(second)));
};
class Detail {
public:
char const* name;
char const* material;
char const* color;
real4 weight;
dbArray< dbReference<Contract> > contracts;
TYPE_DESCRIPTOR((KEY(name, INDEXED|HASHED),
KEY(material, HASHED),
KEY(color, HASHED),
KEY(weight, INDEXED),
RELATION(contracts, detail)));
};
class Contract {
public:
dbDateTime delivery;
int4 quantity;
int8 price;
dbReference<Detail> detail;
dbReference<Supplier> supplier;
TYPE_DESCRIPTOR((KEY(delivery, HASHED|INDEXED),
KEY(quantity, INDEXED),
KEY(price, INDEXED),
RELATION(detail, contracts),
RELATION(supplier, contracts)));
};
</PRE>
Type descriptors should be defined for all classes used in the database.
In addition to defining type descriptors, it is necessary to establish
a mapping between C++ classes and database tables. The macro
<code>REGISTER(</code>name<code>)</code> will do it. Unlike the
<code>TYPE_DESCRIPTOR</code> macro, the <code>REGISTER</code> macro should
be used in the implementation file and not in the header file. It constructs
a descriptor of the table associated with the class. If you are going to work
with multiple databases from one application, it is possible to register
a table in a concrete database by means of the
<code>REGISTER_IN(</code>name,database</code<code>)</code> macro.
The parameter <code>database</code> of this macro should be a pointer to the
<code>dbDatabase</code> object. You can register tables
in the database as follows:<P>
<PRE>
REGISTER(Detail);
REGISTER(Supplier);
REGISTER(Contract);
</PRE>
The table (and correspondent class) can be used only with one database
at each moment of time. When you open a database, FastDB imports into
the database all classes defined in the application.
If a class with the same name already exists
in the database, its descriptor stored in the database is compared with the
descriptor of this class in the application. If the class definitions
differ, FastDB tries to convert records from the table to the new
format. Any kind of conversion between numeric types (integer to
real, real to integer, with extension or truncation) is allowed. Also,
addition of new fields can be easily handled. But removal of fields
is only possible for empty tables (to avoid accidental data destruction).<P>
After loading all class descriptors, FastDB checks if all indices specified
in the application class descriptor are already present in the database,
constructs new indices and
removes indices, which are no more used. Reformatting the table and
adding/removing indices is only possible when no more than one
application accesses the database. So when the first application is attached
to the database, it can perform table conversion. All other applications
can only add new classes to the database.<P>
There is one special internal database <code>Metatable</code>, which
contains information about other tables in the database. C++ programmers
need not access this table, because the format of database tables is specified
by C++ classes. But in an interactive SQL program, it may be necessary to
examine this table to get information about record fields.<P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -