一、介绍
FastDb是高效的内存数据库系统,具备实时能力及便利的C++接口。FastDB不支持client-server架构因而所有使用FastDB的应用程序必须运行在同一主机上。FastDB针对应用程序通过控制读访问模式作了优化。通过降低数据传输的开销和非常有效的锁机制提供了高速的查询。对每一个使用数据库的应用数据库文件被影射到虚拟内存空间中。因此查询在应用的上下文中执行而不需要切换上下文以及数据传输。fastdb中并发访问数据库的同步机制通过原子指令实现,几乎不增加查询的开销。fastdb假定整个数据库存在于RAM中,并且依据这个假定优化了查询算法和接口。此外,fastdb没有数据库缓冲管理开销,不需要在数据库文件和缓冲池之间传输数据。这就是fastdb运行速度明显快于把数据放在缓冲池中的传统数据库的原因。
fastdb支持事务、在线备份以及系统崩溃后的自动恢复。事务提交协议依据一个影子根页面算法来自动更新数据库。恢复可以执行得非常快,为临界应用提供了高可用性。此外,取消事务日志改进了整个系统的性能,并且使得可以更有效的利用系统资源。
fastdb是一个面向应用的数据库,数据库表通过应用程序的类信息来构造。fastdb支持自动的模式评估,使你可以只需要在一个地方更改-你的应用程序的类。fastdb提供一个灵活方便的接口来从数据库中获取数据。使用一个类SQL的查询语言进行指定的查询。通过一些后关系特性如非原子字段,嵌套数组,用户定义类型和方法,对象间直接引用简化了数据库应用程序的设计并使之更有效率。
尽管fastdb的优化是立足于假定整个数据库配置在计算机的物理内存中,但是也有可能出现使用的数据库的大小超过了系统物理内存的大小的情况,在这种情况下标准的操作系统交换机制就会工作。但是整个fastdb的搜索算法和结构是建立在假定所有的数据都存在于内存中的,因此数据换出的效率不会很高。
查询语言
fastdb支持一个类sql句法的查询语言。fastdb使用更流行于面向对象程序设计的表达式而不是关系数据库的表达式。表中的行被认为是对象实例,表是这些对象的一个类。与sql不同,fastdb面向的是对对象的操作而不是对sql元组。所以每一次查询的结果就是来自一个类的对象的集合。fastdb查询语言与标准sql的主要差别在于:
1.不支持多个表之间的连接(join)操作,不支持嵌套子查询。查询总是返回来自一个表的对象的集合。
2.原子表列使用标准的c数据类型
3.没有NULL值,只有null引用。我完全同意C.J.Date对3值逻辑的批评以及他使用缺省值来代替NULL的意图
4.结构和数组可以作为记录元素。一个特别的exists算子(quantor)用来定位数组中的元素
5.可以为表记录(对象)也可以为记录元素定义无参用户自定义方法,
6.应用程序可以定义只有一个串或者数值类型参数的用户自定义函数
7.支持对象间的引用,包括自动支持逆引用
8.通过使用引用,start from follow by执行递归的记录遍历。
9.因为查询语言深度继承在了c++类中,语言标识符和关键字是大小写敏感的
10. 不进行整形和浮点型到串的隐含转换,如果需要这样的转换,必须显式进行
下面类BNF表达式的规则指定了Fastdb查询语言搜索断言的语法:
Grammar conventions
| |
Example
|
Meaning
|
expression
|
non-terminals
|
not
|
terminals
|
|
|
disjoint alternatives
|
(not)
|
optional part
|
{1..9}
|
repeat zero or more times
|
标识符大小写敏感,必须以一个a-z,A-Z,_ 或者$字符开头,只包含a-z, A-Z, 0-9,_或者$字符,不能使用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标准注释,所有位于双连字符后直到行末的字符都将被忽略掉。
fastdb扩展了ansi标准sql操作符,支持位运算。and/of操作符不仅可以运用到布尔操作数也可以操作整形操作书。and/or运用到整形操作数返回的结果是一个整形值,这个值是对操作数进行按位and或者按位or得到的结果。对于小的集合位运算是高效的。fastdb也支持对整形和浮点型的升幂运算(x^y)
fastdb接受结构体作为记录的元组。结构的字段可以通过标准的点表达式访问:company.address.city
结构体的字段可以索引,从而可以按照指定的序列使用。结构体可以包含其他的结构体作为其元组,嵌套深度没有限制。
程序员可以为结构体定义方法,这些方法可以用在查询中,与对普通结构元组的句法是一样的。这些方法除了一个指向其隶属的对象的指针外(C++中的this指针)不能有参数,并且返回原子类型(bool型,数值、字符串或者引用类型)。这些方法也不应该改变对象实例(immutable method).如果方法返回字符串,该字串必须用new字符操作符分配,因为该字串值拷贝之后就会被删掉。
因此用户自定义方法可以用来创建虚元组-不是保存在数据库中而是使用其他元组计算出来的元组。例如:fastdb的dbDateTime类型只包含整形时间戳元组和象dbDateTime::year(), dbDateTime::month()...这样的方法。因此可以在应用中指定象"delivery.year = 1999"这样的查询,其中delivery记录字段拥有dbDateTime类型。方法在应用的上下文中执行,在其中定义,对其他应用和交互SQL是无效的
fastdb接受动态数组作为记录元组,不支持多维数组,但可以定义数组的数组,可以按照结果集中数组字段的长度对记录排序。fastdb提供了一个特别的构造集来处理数组:
1.可以用length()函数来取得数组中元素的数目。
2.数组元素可以用[]操作符来获取。如果索引表达式超出了数组范围,将产生异常
3.in操作符可以用来检查一个数组是否包含有一个由左操作书指定的值。该操作只能用于原子类型的数组:boolean , 数值,引用和字符串。
4.数组可以用update方法更新,该方法复制数组然后返回一个非常量的引用。
5.使用exists运算符迭代数组元素。exists关键字后指定的变量可以作为在exists算子后面的表达式中的数组的索引。该索引变量将迭代所有可能的数组索引值,直到表达式的值为true或者索引越界,下面的情况:
exists i: (contract[i].company.location = 'US')将选择由位于‘US'的公司载运的合同的所有细节,而下面的查询:not exists i: (contract[i].company.location = 'US')将选择由'us'之外的公司载运的合同的所有细节可以由嵌套的exists子句。使用嵌套exist算子等同于使用相应的索引变量的嵌套循环。例如查询 exists column: (exists row: (matrix[column][row] = 0))将选择matrix字段的元素为0的所有记录,该字段拥有整形数组的数组数据类型。这个构造等同于下面的两层嵌套循环:bool result = false; for (int column = 0; column < matrix.length(); column++) { for (int row = 0; row < matrix[column].length(); row++) { if (matrix[column][row] == 0) { result = true; break; } } }
fastdb中的所有字符串都是变长的因此程序员无需费心去指定字符字段的最大长度,所有对数组适用的操作也适用于字符串。此外字符串也有属于自己的操作集。首先,字符串可用标准关系运算符相互比较。目前,fastdb只支持ascii字符集(对应于c的char类型)以及对字符串逐字节的比较而忽略本地设置。
like运算符可以通过一个包含通配符'%'和'_'的模式来匹配字符串,。'_'字符匹配任意的单个字符,'%'匹配0个或多个字符。like运算符的一个扩展形式是与escape关键字一起用来处理模式中的'%'和'_'字符,如果他们出现在一个特定的逃逸字符之后(指escape关键字)就被当作普通字符处理而不是通配符。
可以用in操作符在字符串中查找子串。表达式('blue' in color)对于所有包含color字段包含'blue'的记录都为真。如果被查找的字符串的长度大于某个门槛值(当前为512),则使用boyer-moore子串查找算法而不是直接查找方式。
字符串可以用+或者||运算符进行连接,后者是为了与ansi sql标准的兼容性而加入的。由于fastdb不支持在表达式中隐含的字符串转换,因此+运算符的语义可以为字符串重新定义
References
引用可以用与访问结构元组同样的点表达式来解析,例如下面的查询: company.address.city = 'Chicago'将访问的Contract记录的company元组引用的Supplier表中的记录并展开其中的address字段的city元组。引用可以用is null或is not null断言来检查。引用也可以互相比较是否相等以及与null关键字比较。解析null引用时fastdb将抛出异常。一个特别的关键字current可以用来在表查找时指向当前记录。通常current关键字用来当前记录标志符与其它引用的比较或者在引用数组中定位当前记录。例如,下面的查询将在Contract表中查找所有活动的合同(假定cancelContracts的数据类型为dbArray<dbReference<Contract>>) current not in supplier.canceledContracts
fastdb提供特别的运算符以通过引用来递归遍历记录
the result of the query execution will be:
fastdb中将会写成这样:
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
|
fastdb允许用户自定义函数和运算符。函数应当至少有一个但不超过3个参数,参数类型可以是字符串、整形、布尔型、引用或者用户定义(源二进制)类型。返回值应当是整形、实数、字符串或者布尔型。
用户定义函数应当用USER_FUNC(f)宏来注册,该宏创建一个dbUserFunction类的静态对象,把函数指针和函数名绑定。
在应用中有两种方式来实现这些函数。第一个只能用于只有一个参数的函数,这个参数必须是int8、real8或者char *类型。函数返回值应当是int8、real8、char*或者bool。如果函数有不止一个参数或者接受不同类型的参数(多形)则参数应当以引用的方式传送给dbUserFunctionArgument结构。这个结构包含一个type字段,其值可以用在函数实现中检测传入的参数类型并且与参数值结合。下表映像了参数类型以及参数值的取值。
Argument type
|
Argument value
|
Argument value type
|
dbUserFunctionArgument::atInteger
|
u.intValue
|
int8
|
dbUserFunctionArgument::atBoolean
|
u.boolValue
|
bool
|
dbUserFunctionArgument::atString
|
u.strValue
|
char const*
|
dbUserFunctionArgument::atReal
|
u.realValue
|
real8
|
dbUserFunctionArgument::atReference
|
u.oidValue
|
oid_t
|
dbUserFunctionArgument::atRawBinary
|
u.rawValue
|
void*
|
例如下面语句使得可以在sql语句中使用sin函数。
#include <math.h> ... USER_FUNC(sin);函数只能在定义的应用中使用。函数对于其他应用和交互sql是不可访问的。在返回字符串的函数中,返回的字符串必须用new运算符复制,因为fastdb在拷贝完返回值后将调用析构函数。在fastdb,函数的参数可以(当不是必须)用圆括号括起来,因此下面的表达式都是合法的: '$' + string(abs(x)) length string y 带两个参数的函数也可以当作运算符。考虑下面的例子,其中定义了函数contains进行大小写敏感的字串查找。 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')";在这个例子中,查询q1和q2是等价的。