天天看點

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;

    }

}

FastDB記憶體資料庫API