FastDB内存数据库API

1.  查询语言
mdb支持类SQL句法的查询语言。mdb更接近oop而不是关系数据库。Table中的行被认为是对象实例,table则是这些对象的类。与SQL不同,mdb是面向对象的而不是SQL元组。因此,每次查询的结果是一个类的对象的集合。
标识符大小写敏感,由 a-z, A-Z, '_' 或者 '$'字符开头,只能包含a-z, A-Z, '_' 或者 '$'字符,不能与SQL保留字重复
保留字列表
abs and asc between by
current desc escape exists false
first follow from in integer
is length like last lower
not null or real start
string true upper    

使用ANSI标准注释,在双连字符后直至行尾的字符都被忽略。
mdb支持位操作,and/or不仅可以用于布尔操作数,也可以用于整型操作数。对整型操作数进行and/or操作返回对这两个数进行 位-and/位-or操作的得到的一个整型操作数。
mdb也支持对整型和浮点型的幂运算(x^y)
Structures
结构可以作为记录的组成部分,结构的字段可以通过标准的点表达式访问:
company.address.city
结构字段可以以指定的顺序索引和使用,可以嵌套并且嵌套的层次没有限制。
程序员可以定义结构的方法用于查询。该方法不能有参数并且只能返回原子类型(布尔值、数值、字符串和引用类型)。这些方法也不能改变对象句柄。如果该方法返回字符串,这个字符串应当使用new运算符分配,因为其值拷贝后串就会被删掉。
用户定义方法可以用来创建虚组件-就是不存储在数据库中由别的组件计算出来的组件。例如,mdb dbDateTime类型只包括一个整型的时间戳组件以及如dbDateTime::year(),dbDateTime::month()之类的方法,可以在应用中指定像这样的查询:"delivery.year = 1999",
Arrays
mdb可以使用动态数组作为记录的组成部分。不支持多维数组,但是可以定义数组的数组。
Strings
mdb中的string都是变长的。只支持ascii字符集以及逐字节的比较并忽略字符集的设定。
References
引用通过点表达式来解析,如:company.address.city = 'Chicago',可以用is null和is not null来检查引用是否为空。也可以与null关键字进行是否相等的比较。当解析一个null应用时,mdb会抛出异常。
mdb不支持连接操作,连接操作可以用引用来实现,如下面例子:
struct Detail {
    char const* name;
    double      weight;
   
    TYPE_DESCRIPTOR((KEY(name, INDEXED), FIELD(weight)));
};
 
struct Supplier {
    char const* company;
    char const* address;
 
    TYPE_DESCRIPTOR((KEY(company, INDEXED), FIELD(address)));
};
 
struct Shipment {
    dbReference<Detail>   detail;
    dbReference<Supplier> supplier;
    int4                  price;
    int4                  quantity;
    dbDateTime            delivery;
 
    TYPE_DESCRIPTOR((KEY(detail, HASHED), KEY(supplier, HASHED),
                    FIELD(price), FIELD(quantity), FIELD(delivery)));
};
我们需要得到来自于某个suppliers的delivery的details信息,在关系数据库中有如下查询:
     select from Supplier,Shipment,Detail where
                 Supplier.SID = Shipment.SID and Shipment.DID = Detail.DID
                and Supplier.company like ? and Supplier.address like ?
                and Detail.name like ?
在mdb中就写成这样:
     dbQuery q = "detail.name like",name,"and supplier.company like",company,
                 "and supplier.address like",address,"order by price";
Functions
Predefined functions
Name Argument type Return type Description
abs integer integer absolute value of the argument 
abs real real absolute value of the argument 
integer real integer conversion of real to integer 
length array integer number of elements in array 
lower string string lowercase string 
real integer real conversion of integer to real 
string integer string conversion of integer to string 
string real string conversion of real to string 
upper string string uppercase string 

mdb允许用户自行定义函数和运算符,函数至少需要一个参数,但不能超过3个。参数类型必须为string、integer、boolean、reference或者用户定义类型(raw binary)
用户自定义函数必须使用USER_FUNC(f)宏注册。
 
2.  C++接口
mdb中可以用c++写出如下的查询:        
    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());
 
Table
mdb的数据存储在table中,这对应于C++类,表记录则对应于类实例。下面是可以做为mdb记录原子组件的C++类型:
Type Description
bool boolean type (true,false)
int1 one byte signed integer (-128..127)
int2 two bytes signed integer (-32768..32767)
int4 four bytes signed integer (-2147483648..2147483647)
int8 eight bytes signed integer (-2**63..2**63-1)
real4 four bytes ANSI floating point type
real8 eight bytes ANSI double precision floating point type
char const* zero terminated string
dbReference<T> reference to class T
dbArray<T> dynamic array of elements of type T

除此之外,mdb记录也包括包含这些componets的嵌套的结构。mdb不支持unsigned类型。
下面是定义一个table的例子,可以放在头文件中:
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)));
};
定义了表之后,就需要用REGISTER(name)宏来注册,也就是把上面定义的类与表联系起来:
REGISTER(Detail);
REGISTER(Supplier);
REGISTER(Contract);
这个宏应该放在实现而不是头文件中。
当数据库新注册的表与库中原有的表同名时,mdb会比较两个表,如果定义不同,mdb会把原来的表更新成新的表,可以添加新字段,但是删除字段只有在原来的表为空的时候才可以进行。
有一个特殊的内部数据库表Metatable,保存了这个数据库中其他所有表的信息。
支持自动增加字段。
Query
The class query is used to serve two purposes:
to construct a query and bind query parameters
to cache compiled queries
例子:
        dbQuery q;
        int price, quantity;
        q = "price >=",price,"or quantity >=",quantity;
给上面的参数price,quantity赋不同的值查询可以得到不同的结果,不能用数值常量作查询参数,但是可以用字符串常量作查询参数。
 
有两个方法来给字符串类型的参数赋值:
     dbQuery q;
     char* type;
     char name[256];
     q = "name=",name,"and type=",&type;
 
     scanf("%s", name);
     type = "A";    
     cursor.select(q);
     ...
     scanf("%s", name);
     type = "B";    
     cursor.select(q);
     ...
Cursor
Cursors用于访问查询语句返回的记录。mdb提供有类型的cursor,即与具体表关联的cursor.mdb有两种cursor:只读cursor和用于更新的cursor.mdb中cursor用c++模板类dbCursor<T>表示,T为一个与表关联的C++类的名字。cursor的类型必须在构造时指定,缺省时只读的。要创建一个用于更新的cursor,必须向constructor传递参数dbCursorForUpdate,
如:dbCursor<Contract> contract(dbCursorForUpdate);
 
查询通过执行cursor的select(dbQuery& q)或者select()方法,后者用来迭代表中的所有记录。两个方法都返回查询选择的记录数并且置cursor的当前位置为第一个记录(如果存在的话)。一个cursor可以向前向后滚动,next(),prev(),first(),last()方法用来改变cursor的当前位置,如果由于没有更多记录而使得这些操作进行下去,则这些操作返回NULL,并且当前的cursor位置不变。
 C++ API定义了null引用类型,可以将null与reference比较或者将其赋给reference:
        void update(dbReference<Contract> c) {
            if (c != null) {
                dbCursor<Contract> contract(dbCursorForUpdate);
               contract.at(c);
               contract->supplier = null;
            }
        }
 
query参数通常与C++变量绑定,解决多线程同一query不同参数执行的问题 ,mdb采用了延迟参数绑定的方法:
dbQuery q;
struct QueryParams {
    int         salary;
    int         age;
    int         rank;
};
void open()
{
    QueryParams* params = (QueryParams*)NULL;
    q = "salary > ", params->salary, "and age < ", params->age, "and rank =", params->rank;
}
void find(int salary, int age, int rank)
{
    QueryParams params;
    params.salary = salary;
    params.age = age;
    params.rank = rank;
    dbCursor<Person> cusor;
    if (cursor.select(q, & params) > 0) {
        do {
        } while (cursor.next());
    }
}
So in this example function open binds query parameters just to offsets of fields in structure. Later in find functions, actual pointer to the structure with parameters is passed to the select structure. Function find can be concurrently executed by several threads and only one compiled version of the query is used by all these threads. This mechanism is available since version 2.25.
Database
dbDatabase类控制应用与数据库的交互。同步数据库的并发访问,事务管理,内存分配,错误处理,。。。
构造dbDatabase对象时可以指定数据库参数:
    dbDatabase(dbAccessType type = dbAllAccess,
               size_t dbInitSize = dbDefaultInitDatabaseSize,
               size_t dbExtensionQuantum = dbDefaultExtensionQuantum,
               size_t dbInitIndexSize = dbDefaultInitIndexSize,
               int nThreads = 1);
当database主要是以readonly模式访问并且更新不能长时间锁定读的时候应当同时使用dbConcurrentUpdate和dbConcurrentRead模式。在这种模式下,更新数据库可以与读访问并发进行(reader将看不到改变的数据,直到事务提交),只有在事务提交时才设置排它锁并且当更新了当前对象的索引后会马上释放。
Attension! Do not mix dbConcurrentUpdate and dbConcurrentRead mode with other modes and do not use them together in one process (so it is not possible to start two threads in one of which open database in dbConcurrentUpdate mode and in other - in dbConcurrentRead). Do not use dbDatabase::precommit method in dbConcurrentUpdate mode.
使用open(char const* databaseName, char const* fileName = NULL, unsigned waitLockTimeout = INFINITE)方法打开数据库。如果fileName为空,则自动在databaseName加上'.fdb'形成数据库文件名,waitLockTimeout用来设置一个进程被锁住的最大时间,当过期后,被锁住的活动进程就自动恢复执行。
使用dbDatabase::backup(char const* file)方法备份数据库。恢复只需要改文件名。
 
3.  SubSql
   select (*) from table-name select-condition ;
  | insert into table-name values values-list ;
  | create index on on table-name.field-name ;
  | create table table-name (field-descriptor {, field-descriptor}) ;
  | alter table table-name (field-descriptor {, field-descriptor}) ;
  | update table-name set field-name = expression {, field-name = expression} where condition ;
  | drop index table-name.field-name ;
  | drop table table-name
  | open database-name ( database-file-name ) ;
  | delete from table-name
  | backup file-name
  | start server server-URL number-of-threads
  | stop server server-URL
  | start http server server-URL
  | stop http server server-URL
  | export xml-file-name
  | import xml-file-name
  | commit
  | rollback
  | autocommit (on | off)
  | exit
  | show
  | help
 
 
4.  Quick start
开发mdb应用时,首先要确定需要存储在数据库中的数据和类。通过REGISTER宏注册表标识符。如果想要重定义缺省的mdb出错处理,就要从dbDatabase继承定义自己的数据库类。应当创建一个该类的实例并且使之能够让所有的应用模块访问。
在操作数据库前,首先要打开。检查dbDatabase::open()的返回码确定数据库是否成功返回。在数据库打开过过程中出错并不会中止应用不过回报告。
确认数据库正常打开后,就可以开始工作。如果是多线程的应用并且多个线程会使用同一个数据库,就用dbDatabase::attach方法把每一个线程与数据库连接起来。在线程终止前,应该通过dbDatabase::detach()方法将其与数据库剥离。
为了访问数据库的数据,需要创建一些dbQuery和dbCursor对象,如果多个线程同时工作于一个数据库,每一个线程都要有自己query和cursor对象。通常一个表有一个cursor就够了(或者两个,如果应用需要更新数据库的话)。但在嵌套查询时也需要使用多个cursor 。每一种类型的查询都需要创建一个query对象,query对象也用来缓存编译好的查询。
数据库有4个主要操作:insert, select, update, remove.
第一个操作不需要cursor,而使用全局重载的模版函数insert。
选择,更新和删除记录需要使用cursor。要改变表就必须使用用于更新的cursor. 在mdb中cursor是类型化的,并且包含一个table类的对象实例。重载的cursor运算符‘->'能够用来访问当前记录的组件,也能够用来更新这些组件。update方法把当前cursor对象的数据复制到当前的表记录中。remove方法将移除当前cursor记录,removeAllSelected将移除所有中选的记录,removeAll将移除表中所有的记录。每一个事务由dbDatabase::commit()提交或者由dbDatabase::rollback()中止。在第一个select 、insert或者remove操作执行时就自动开始一个事务。
 在退出应用前要记住关闭数据库,同时也要记得dbDatabase::close()方法会自动提交最后一个事务。因此如果这不是你所想的操作,推出前显式调用dbDatabase::rollback
一个mdb应用的模版:
//
// Header file
//
#include "mdb.h"
 
extern dbDatabase db; // create database object
 
class MyTable {
    char const* someField;
    ...
  public:
    TYPE_DESCRIPTOR((FIELD(someField)));
};
 
//
// Implementation
//
REGISTER(MyTable);
 
int main()
{
    if (db.open("mydatabase")) {
        dbCursor<MyTable> cursor;
        dbQuery q;
 
  char value[bufSize];
 
  q = "someField=",value;
  gets(value);
  if (cursor.select(q) > 0) {
      do {
          printf("%s\n", cursor->someField);
      } while (cursor.next());
        }
  db.close();
  return EXIT_SUCCESS;
    } else {
        return EXIT_FAILURE;
    }
}


原文地址:https://www.cnblogs.com/yanzhenan/p/1437206.html