快速开始
第一步:数据库定义
使用 C 语言 API 的数据库应用程序,在主机环境中的开发包括三个阶段:
数据布局定义(创建所谓的数据库模式)和编译(生成数据库接口文件,包括数据库字典;数据库字典是数据库模式的二进制表示,SmartEDB运行时使用它来理解数据布局、字段、索引等)。
在应用程序中使用静态和特定于模式生成的 API。
将数据库接口文件与应用程序源代码一起编译,并与需要的运行时库链接。
构建 C 应用程序的这些基本步骤在以下图表中说明;主机组件为蓝色,目标组件为红色:

模式定义
数据库模式是在类似于高级 C 语言的数据定义语言(DDL)中定义的。基本的 DDL 语句是 declare database <db_name>
来分配数据库名称,以及 class <class_name>
来定义数据库类(对应于 SQL 术语中的表)。例如,以下定义了一个名为 mydb
的简单数据库,以及一个单一的类(表)MyClass
,具有两个字段 id
(一个非负 4 字节整数)和 name
(一个可变长度字符串)。
示例:
declare database mydb;
class MyClass {
unsigned<4> id;
string name;
};
模式编译
通常,模式文件的扩展名为.mco
;例如 schema.mco
或 mydb.mco
。然后,该文件由 DDL 编译器 mcocomp
进行编译,以生成特定于模式的数据库字典和数据访问 API,形式为 <db_name>.h
头文件和 <db_name>.c
实现文件。例如,以下命令将在当前工作目录中生成文件 mydb.h
和 mydb.c
:
../../host/bin/mcocomp mydb.mco
请参阅SDK示例目录下文件 samples/native/core/00_ddl/schema.mco
。
在应用程序源代码中实现
在 samples/native/common
目录中提供了许多“帮助”函数,可以方便初始开发。最简单的 SmartEDB 应用程序只需包含 common.h
头文件,然后调用辅助函数 sample_os_initialize()
,打印出“**Hello world”**类型的消息,调用 sample_pause_end()
暂停执行,然后调用 sample_os_shutdown()
终止。
请参阅SDK示例目录下文件:samples/native/core/00_ddl/main.c
。
编译和链接应用程序
在常规内存中创建 SmartEDB 数据库的简单应用程序将需要以下库(按照惯例,我们使用缩写名称,例如,mcolib
,在 Windows 平台上它将以 mcolib.lib
存在,在 Unix-Linux 平台上以 libmcolib.a
存在):
库 | 说明 |
---|---|
mcolib | 核心运行时库。 |
mcovtmem | 用于全内存数据库“虚拟表”库。 |
mcomconv | 用于常规内存“内存设备”库。 |
mcosw32, mcosw32n, or mcoslnx | 同步库。 |
mcouwrt | 实用程序库。 |
请参阅SDK示例 samples/native/core/00_ddl
,以及构建与运行SDK示例了解具体操作。
第二步:运行时信息
检查运行时信息是很有用的。请构建并运行此示例,并注意显示的信息。在源文件 main.c
中,请注意调用了许多“示例辅助”函数。除了在步骤 1 中介绍的 sample_os_initialize()
、sample_pause_end()
和 sample_os_shutdown()
之外,还使用了函数 sample_header()
来显示标题消息,并调用了 sample_show_runtime_info()
来显示运行时信息。
这些辅助函数在文件 common.c
中实现,并封装了许多标准操作,从而简化了示例应用程序代码。在这里,检查函数 sample_show_runtime_info()
以了解如何从通过调用静态 API 函数 mco_get_runtime_info()
返回的 mco_runtime_info_t
结构中检索运行时信息是很有启发性的。
请参阅SDK示例 samples/native/core/01_rtconfig_inmem
。
第三步:打开数据库
在执行数据存储和检索操作之前,我们必须打开数据库。对于全内存数据库,可以有一个设备来描述数据库的常规(又名本地或进程内)或共享内存。
SDK示例
samples/native/core/02_open_conv
演示了如何在“常规”内存中打开全内存数据库。samples/native/core/02_open_dbextend
演示了使用设备数组来允许应用程序扩展内存数据库。
数据库字典是模式的编译形式,可通过生成的函数 <dbname>_get_dictionary()
进行访问。此函数通常在调用 mco_db_open_dev()
时作为第二个参数调用。
以下代码片段演示了初始化内存设备和数据库参数,然后打开内存数据库的过程:
/* Setup memory device as a plain conventional memory region */
dev.type = MCO_MEMORY_CONV; /* Set the device as a conventional memory device */
dev.assignment = MCO_MEMORY_ASSIGN_DATABASE; /* Assign the device as main database memory */
dev.size = DATABASE_SEGMENT_SIZE; /* Set the device size */
dev.dev.conv.ptr = (void*)malloc( DATABASE_SEGMENT_SIZE ); /* Allocate memory and set device pointer */
/* Initialize and customize the database parameters */
mco_db_params_init ( &db_params ); /* Initialize the params with default values */
db_params.mem_page_size = MEMORY_PAGE_SIZE; /* Set page size for the in-memory part */
db_params.disk_page_size = 0; /* Set page size to zero to disable disk operations */
db_params.db_max_connections = 1; /* Set total number of connections to the database */
/* Open a database on the device with given params */
rc = mco_db_open_dev(db_name, convdb_get_dictionary(), &dev, 1, &db_params );
if ( MCO_S_OK == rc )
{
...
}
对于持久数据库,要打开数据库必须定义至少四个存储设备。C 语言应用程序使用包括数据库字典、数据库参数结构和内存设备数组等多个参数调用函数 mco_db_open_dev()
。
SDK示例
samples/native/02_open_shared
演示打开基于共享内存设备数据库。samples/native/core/02_open_disk_file
演示打开常规基于文件内存设备的数据库。
与常规内存库 mcomconv
不同,平台相关的共享内存库 mcomipc
或 mcomw32
被链接到 02_open_shared 中。
注意
如果您通过替换 mcomconv
更改链接器指令,数据库打开函数 mco_db_open_dev()
时将因无效参数而失败。这是因为共享内存设备 dev.type = MCO_MEMORY_NAMED
以及参数 dev.named.name
仅对共享内存有效,这在 mcomipc
(Unix-Linux)、mcompsx
(对于具有 POSIX API 的操作系统,如 QNX 或 VxWorks)或 mcomw32
(Win32)库中实现,而不是 mcomconv
中实现。
:::cautions 关闭数据库
请记住,一旦打开了数据库,应用程序在终止之前应通过调用 mco_db_close()
关闭它。
:::
定义存储设备
对于内存数据库,可以有一个设备来描述数据库的常规(又名本地或进程内)或共享内存,而对于持久数据库或混合数据库(包含瞬态和持久类),必须定义至少四个设备。例如:
const char * db_name = "C:\\data\\myDb";
#define N_DEVICES 4
#define DATABASE_SIZE (600 * 1024 * 10)
#define CACHE_SIZE (300 * 1024 * 10 )
mco_device_t dev[N_DEVICES];
/* Configure first memory device as a plain conventional memory region */
dev[0].type = MCO_MEMORY_CONV;
dev[0].assignment = MCO_MEMORY_ASSIGN_DATABASE;
dev[0].size = DATABASE_SIZE;
dev[0].dev.conv.ptr = (void*)malloc( DATABASE_SIZE );
/* Configure conventional memory region for cache */
dev[1].type = MCO_MEMORY_CONV;
dev[1].assignment = MCO_MEMORY_ASSIGN_CACHE;
dev[1].size = CACHE_SIZE;
dev[1].dev.conv.ptr = (void*)malloc( CACHE_SIZE );
/* Configure FILE memory device for main database storage */
dev[2].type = MCO_MEMORY_FILE;
dev[2].assignment = MCO_MEMORY_ASSIGN_PERSISTENT;
sprintf(dev[2].dev.file.name, FILE_PREFIX "%s.dbs", db_name);
dev[2].dev.file.flags = MCO_FILE_OPEN_DEFAULT;
/* Configure FILE memory device for transaction log */
dev[3].type = MCO_MEMORY_FILE;
dev[3].assignment = MCO_MEMORY_ASSIGN_LOG;
sprintf(dev[3].dev.file.name, FILE_PREFIX "%s.log", db_name);
dev[3].dev.file.flags = MCO_FILE_OPEN_DEFAULT;
数据库参数
数据库打开参数在结构 db_params_t
中定义。通常,通过调用 mco_db_params_init()
来初始化参数以设置默认值,然后分配以下四个特定于应用程序的参数:
#define MEMORY_PAGE_SIZE 128
#define PSTORAGE_PAGE_SIZE 4096
mco_db_params_t db_params;
/* Initialize and customize the database parameters */
mco_db_params_init ( &db_params ); /* Initialize the params with default values */
db_params.mem_page_size = MEMORY_PAGE_SIZE; /* Set page size for in-memory part */
db_params.disk_page_size = PSTORAGE_PAGE_SIZE; /* Set page size for persistent storage */
db_params.db_max_connections = 1; /* Set total number of connections to the database */
db_params.db_log_type = UNDO_LOG; /* Set log type */
数据库操作
一旦指定了持久数据库设备和参数,数据库打开调用 mco_db_open_dev()
、连接调用 mco_db_connect()
以及执行数据库操作的所有其他 API 都与内存数据库的相同。
第四步:连接数据库
打开数据库后的下一步是创建连接。
- SDK示例:samples/native/core/03_connect_single_task 。该示例演示了在成功打开数据库后,如何调用函数
mco_db_connect()
来创建数据库连接。一旦成功建立连接,连接句柄可以传递给同一线程内的各种应用函数以执行数据库操作。完成后,在关闭数据库和终止应用程序之前,应始终调用函数mco_db_disconnect()
来关闭连接。 - SDK示例:samples/native/core/03_connect_multi_task 。该示例演示了如何由多个线程使用打开的数据库的句柄来创建到同一数据库的单独连接。请注意,一个常见的错误是将单个数据库连接传递给多个线程。这必须避免 - 每个任务(线程)都必须调用
mco_db_connect()
来创建自己的单独连接。 - SDK示例:samples/native/core/03_connect_multi_process 。该示例演示了如何通过首先调用
mco_db_connect()
来检测数据库是否已经打开。如果返回错误代码 MCO_E_NOINSTANCE,则数据库尚未打开,因此应用程序打开并连接。否则,对mco_db_connect()
的初始调用创建连接,应用程序按预期进行。要查看示例的运行情况,需要运行第一个进程,然后启动程序的第二个实例(使用单独的控制台)。
:::cautions 关闭数据库
请记住,一旦创建了数据库连接,在关闭数据库之前,应用程序必须通过调用 mco_db_disconnect()
来关闭它。
:::
第五步:数据访问操作
一旦打开数据库并建立连接,应用程序就可以开始执行读写存储操作。
- SDK 示例:samples/native/core/04_operations 。该实例演示了通过调用函数
mco_trans_start()
启动数据库事务。一旦打开事务,事务句柄将传递给生成的classname_new()
函数,以在数据库中创建对象的新实例。对象的句柄用于生成的classname_fieldname_get()
和classname_fieldname_put()
操作,以将值插入对象。当使用事务句柄调用mco_trans_commit()
时,新值将存储在数据库中。
第六步:索引和游标
索引根据某些搜索条件为数据库对象的快速查找和/或排序检索提供了支持,而游标则为遍历对象的结果集提供了方法。
- SDK 示例: samples/native/core/05_indexes* 展示了 SmartEDB所支持的各种强大的索引。
第七步:错误处理
返回代码包含运行时返回代码的三个类别:状态代码、非致命错误代码和致命错误代码。
SDK 示例:samples/native/core/06_errorhandling_statuscode 展示了状态返回代码,以及应用程序如何检查各种运行时函数调用的状态。该示例强调了像
MCO_S_NOTFOUND
、MCO_S_DUPLICATE
和MCO_S_CURSOR_END
这样的状态代码可能是正常操作的一部分。SDK 示例:samples/native/core/06_errorhandling_nonfatalerr 演示了非致命错误情况。
SDK 示例:samples/native/core/06_errorhandling_fatalerr 演示了致命错误情况。致命错误情况需要特殊处理,而且很可能指出实现错误,通常的做法是注册一个致命错误处理程序,在遇到严重的运行时错误时停止应用程序,如前面的示例所示。