索引和游标
如索引和游标页面中所述,SmartEDB 支持多种索引类型。以下部分提供了管理这些索引类型的 C API 实现细节:B树、Patricia、R树、KD树、Hash、OID、autoid 和 自定义。
没有索引则无法访问 SmartEDB 类,因此每个类必须至少定义一个索引。如果未显式定义索引,并且该类不包含动态大小的字段(例如字符串、向量或可选结构体等),则将使用 fixedrec 分配器为该类创建列表索引。通过 fixedrec 分配器,所有数据库对象实例的位置是已知的,这使得可以通过对象链而不是 B 树来访问这些对象。与标准的 B 树索引相比,这种列表索引实现的性能开销最小。
请注意,隐式创建列表索引仅适用于具有固定大小字段的类。如果类包含一个或多个动态字段,则无法使用 fixedrec 分配器生成“对象链”。因此,当类包含动态字段时,应用程序必须显式定义一个列表索引或其他适当的索引类型。
C应用特性
- C应用程序可以使用包容性声明来优化B树索引性能。
- C应用程序可用的另一个附加特性是能够在索引的模式定义中使用自愿限定符,以指示可以在运行时创建或删除索引。在应用程序显式调用生成的函数
<classname_indexname>create()
之前,不会构建自愿索引。以同样的方式,应用程序可以通过调用生成的函数<classname_indexname>drop()
来删除一个自愿索引。 - 此外,在C应用程序中也可以使用SmartEDB专用对象ID (oid)索引。
- C开发人员可以使用的另一个重要特性是定义自定义索引的能力。
对于持久数据库,有一些优化特性特别适用于B树索引。请参阅C语言中的持久数据库索引优化页面了解更多细节。
B树索引
B-树索引可用于有序(排序)检索、范围检索和模式匹配。在模式文件中,B 树索引通过树声明以及可选的唯一或非唯一修饰符来指定。如果未指定修饰符,则默认为非唯一。
例如:
class anObject
{
uint4 value;
tree <value> Idx;
};
mcocomp模式编译器为树索引生成游标函数和搜索函数。
获取树索引的游标:
MCO_RET classname_indexname_index_cursor(
/*IN*/ mco_trans_h t,
/*OUT*/ mco_cursor_h c
);
为所有树索引生成的_search()
函数格式如下:
MCO_RET classname_indexname_search(
/*IN*/ mco_trans_h trans,
/*INOUT*/ mco_cursor_h cursor,
/*IN*/ MCO_OPCODE op, // 游标操作符代码中定义的比较操作
/*IN*/ [const] <type> [*]param1,
[[/*IN*/ uint2 len1,]
[/*IN*/ [const] <type> [*]param2,
[/*IN*/ uint2 len2,] …
]);
从当前光标位置获取对象句柄:
MCO_RET classname_from_cursor(
/*IN*/ mco_trans_h t,
/*IN*/ mco_cursor_h c,
/*OUT*/ classname *object
);
注意,游标必须用游标定位函数正确初始化,以检索有效的数据库对象句柄。
locate()
函数用于根据对象引用定位树索引游标。游标必须是先前使用index_cursor()
函数实例化的。(注意_locate()
函数仅适用于基于树的游标,而不适用于列表或哈希游标):
MCO_RET classname_indexname_locate(
/*IN*/ mco_trans_h t,
/*INOUT*/ mco_cursor_h c,
/*IN*/ classname * handle
);
游标和搜索
标准的游标定位函数mco_cursor_first()
、mco_cursor_last()
、mco_cursor_next()
和mco_cursor_prev()
用于将游标初始化为第一个或最后一个,并遍历搜索的结果集。
使用生成的_search()
函数执行搜索。请参阅搜索页面了解实现细节。此外,正如搜索页面中所解释的,树索引可用于模式匹配、范围检索和有序(排序)检索。
请参阅搜索页面了解实现细节。
优化树索引
通常,B树实现不会在索引中存储键值。然而,对于内存数据库,有时将键值保留在索引页上是有益的。
如键值包含索引和覆盖索引页面中所述,可以通过声明索引包容性来优化B树索引性能。
类的键值包含索引是通过包含关键字在C API模式中定义的,例如:
class Tab
{
string name;
int4 code;
string body;
inclusive tree<name,code> pk;
};
如果想让所有的B树索引为瞬态类键值包含索引,MCO_DB_INCLUSIVE_BTREE
选项可以在C API mco_db_open_dev()
的 db_params
参数中指定,例如:
MCO_RET rc;
mco_db_h db = 0;
mco_device_t dev[N_DEVICES];
mco_db_params_t db_params;
...
db_params.mode_mask |= MCO_DB_INCLUSIVE_BTREE;
...
rc = mco_db_open_dev(db_name, mydb_get_dictionary(), dev, N_DEVICES, &db_params );
此外,覆盖索引也可以声明为包含,以利用CPU缓存使用,例如:
class Tab
{
string name;
int4 code;
string body;
inclusive tree<*> pk;
};
请注意,键值包含索引和覆盖索引只能是B树索引。
Patricia trie索引
SmartEDB的Patricia索引在网络和电信应用中特别有用。Patricia索引可以声明在标量和布尔数据类型及其数组和向量上。实际上,布尔数据类型支持使用位数组来存储IP地址。Patricia索引可以被声明为唯一;如果没有指定unique
关键字,则默认允许重复键值。与SmartEDB的其他索引不同,Patricia索引不能是复合索引,它始终针对单个字段进行声明。
下面的模式说明了一些可能的声明:
class xyz
{
boolean b1[32];
vector<boolean> b2;
uint4 b3;
char<10> b4[10];
vector<string> b5;
patricia <b1> Ib1;
patricia <b2> Ib2;
patricia <b3> Ib3;
patricia <b4> Ib4;
patricia <b5> Ib5;
unique patricia <b1> Ib1U;
unique patricia <b2> Ib2U;
unique patricia <b3> Ib3U;
unique patricia <b4> Ib4U;
unique patricia <b5> Ib5U;
}
请注意,布尔字段b1
、b2
被用作Patricia索引Ib1U
和Ib2U
的位数组。mcocomp模式编译器以一种特殊的方式处理布尔数据类型,需要在下一节中进行一些解释。此外,除了标准树索引生成的函数之外,下一节还将讨论为每个Patricia索引生成的函数。
布尔数据类型
布尔数据类型可用于定义单个位字段、固定大小的位数组或可变长度的位数组。布尔数据类型唯一可能的索引类型是patricia索引。不能对单个位字段进行索引,也不建议对短位数组进行索引。
例如:
class xyz
{
boolean b1; // 位域
boolean b2[32]; // 固定大小的32位数组
vector<boolean> b3; // 可变长度位数组
};
对于C应用程序,为布尔类型的字段生成以下函数:
classname_fieldname_get(classname *handle, /*OUT*/ uint1 * result);
classname_fieldname_put(classname *handle, uint1 value);
对于固定大小的位数组,还会生成以下接口:
// 从数组中读取指定的位
// 读取一位是可以的,但如果要读取多位,最好读取整个数组并进行掩码操作。
classname_fieldname_at(classname* handle, uint2 index, /*OUT*/ uint1 *result);
// 读取位范围
// 注意起始索引位号必须是 8 的幂次方
classname_fieldname_get_range(classname* handle, uint2 start_index, uint2 num,
/*OUT*/ uint1 *dest );
// 同样,逐位的 _put 并非写入数组的最有效方式。最好对整个数组进行掩码处理并使用 _put_range 。
classname_fieldname_put(classname* handle, uint2 index, uint1 value );
// 写入位范围
// 注意起始索引位号必须是 8 的幂次方
classname_fieldname_put_range(classname* handle, uint2 start_index,
uint2 num, const uint1 *src );
// 读取整个字段
classname_fieldname_get(classname *handle, /*OUT*/ uint1 * result);
对于可变大小的数组(向量),还会生成以下接口:
// 向量大小(以位为单位)
classname_fieldname_size(classname* handle, /*OUT*/ uint2 *result );
// 分配一个向量(大小以位为单位)
classname_fieldname_alloc(classname* handle, uint2 size);
// 在指定位置读取一个元素
classname_fieldname_at(classname* handle, uint2 index,
/*OUT*/ uint1 *result);
// 读取位的范围
//注意start_index位必须是8的幂
classname_fieldname_get_range(classname* handle, uint2 start_index,
uint2 num, /*OUT*/ uint1 *dest );
// 写入位范围
// 注意起始索引位号必须是 8 的幂次方
classname_fieldname_put_range(classname* handle, uint2 start_index,
uint2 num, const uint1 *src );
// 写入一个元素(效率不高)
classname_fieldname_put(classname* handle, uint2 index, uint1 value );
生成函数
生成的仅适用于Patricia索引的函数是longest_match()
、exact_match()
、prefix_match()
和next_match()
。根据被索引字段的类型形式会略有不同。
在标量字段上创建Patricia索引将导致生成以下函数:
classname_indexname_next_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
type mask, int number_of_bits);
classname_indexname_prefix_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
type mask, int number_of_bits);
classname_indexname_longest_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
type mask, int number_of_bits);
classname_indexname_exact_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
type mask, int number_of_bits);
其中,type是标量类型(例如uint4), mask
是要匹配的键值。
如果索引字段是固定长度的数组或标量向量,则这些函数的形式为:
classname_indexname_next_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
type * mask, int number_of_bits);
classname_indexname_prefix_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
type * mask, int number_of_bits);
classname_indexname_longest_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
type * mask, int number_of_bits);
classname_indexname_exact_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
type * mask, int number_of_bits);
这里type
是数组或向量的每个元素的类型(例如uint4), mask
是要匹配的键值。
如果索引字段是一个布尔数组,这些函数将是这样的形式:
classname_indexname_next_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
char* mask, int number_of_bits);
classname_indexname_prefix_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
char* mask, int number_of_bits);
classname_indexname_longest_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
char* mask, int number_of_bits);
classname_indexname_exact_match( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
char* mask, int number_of_bits);
这里mask
是一个键值,它是一个打包的位数组(每个字节包含8位)。
游标和搜索
标准游标定位函数mco_cursor_last()
、mco_cursor_next()
和mco_cursor_prev()
用于遍历Patricia索引搜索的结果集。但是不是使用search()
函数,而是使用生成的match()
函数之一执行搜索。最初,游标的位置没有定义,需要通过查找或调用mco_cursor_first()
或mco_cursor_last()
来设置。
请参阅搜索页面了解实现细节。
R树索引
R树索引通常用于加速空间搜索。
R树索引通常是为包含描述“矩形”所需的坐标数的数组字段定义的。例如:
class rtree_class
{
int2 square[4];
rtree <square> ridx;
};
为该类生成的R树索引相关函数如下:
MCO_RET rtree_class_ridx_index_cursor(
mco_trans_h t,
/*OUT*/ mco_cursor_h c
);
MCO_RET rtree_class_ridx_search (
mco_trans_h t_, /*IN*/ MCO_OPCODE op_,
/*INOUT*/ mco_cursor_h c_,
const int2* square
);
游标和搜索
使用生成的_search()
函数执行R树搜索,该函数带有四个搜索操作码之一:MCO_EQUAL
、MCO_CONTAIN
、MCO_OVERLAP
或MCO_NEIGHBORHOOD
。
请参阅搜索页面了解实现细节。
KD树索引
KD树索引是多维键值搜索的理想选择。使用KD树声明在模式中定义KD树索引。例如:
class Car
{
string vendor;
string model;
string color;
uint4 year;
uint4 mileage;
boolean automatic;
boolean ac;
uint4 price;
char<3> state;
string description;
kdtree <year, mileage, color, model, vendor, automatic, ac, price> index;
};
为这个类生成的与KD索引相关的函数如下:
MCO_RET Car_index_index_cursor ( mco_trans_h t, /*OUT*/ mco_cursor_h c );
MCO_RET Car_index_search ( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
/*IN*/ Car * range_start, /*IN*/ Car * range_end );
MCO_RET Car_index_locate ( mco_trans_h t, /*OUT*/ mco_cursor_h c, Car * handle );
游标和搜索
正如在KD树 Index页面中所解释的,使用KD索引执行搜索是使用按例查询的方法。一旦通过搜索操作找到了数据库对象的选择,就使用标准的光标定位函数mco_cursor_first()
、mco_cursor_last()
、mco_cursor_next()
和mco_cursor_prev()
来遍历结果集。
请参阅搜索页面了解实现细节。
三元索引
当无法精确知道目标对象的确切拼写时,三元索引是文本搜索的理想选择。使用三元声明在模式中定义三元索引。例如:
class anObject
{
uint4 id;
string text;
trigram<text> trigramIdx;
};
为这个类生成的与三元组索引相关的函数如下:
MCO_RET anObject_trigramIdx_index_cursor (
mco_trans_h t,
/*OUT*/ mco_cursor_h c
);
MCO_RET anObject_trigramIdx_search (
mco_trans_h t,
/*INOUT*/ mco_cursor_h c,
MCO_OPCODE op_,
const char *text_key_,
uint2 sizeof_text_key_
);
MCO_RET anObject_trigramIdx_locate (
mco_trans_h t,
/*OUT*/ mco_cursor_h c,
anObject * handle
);
游标和搜索
使用生成的_search()
函数执行具有三元组索引的搜索。一旦通过搜索操作找到了数据库对象的选择,就使用标准的游标定位函数mco_cursor_first()
、mco_cursor_last()
、mco_cursor_next()
和mco_cursor_prev()
来遍历结果集。
请参阅搜索页面了解实现细节。
哈希索引
哈希索引是快速查找单个数据库对象的理想选择。哈希索引需要在索引名后面加上一个额外的参数expected-number-of-entries
。它是一个整数,运行时使用它为索引分配初始哈希表。
例如:
class Record
{
uint4 iIdx; /* 索引 */
uint4 iSeries; /* 测量系列 */
hash <iIdx> I_Index[10000];
nonunique hash <iSeries> I_Series[10000];
};
注意,索引I_Index
和I_Series
的[10000]规范会导致运行时为初始哈希表分配10000个键值的空间。
在这个类中,为(唯一的)哈希I_Index
生成的索引相关函数如下:
MCO_RET Record_I_Index_index_cursor (
mco_trans_h t,
/*OUT*/ mco_cursor_h c
);
MCO_RET Record_I_Index_compare (
mco_trans_h t,
mco_cursor_h c,
uint4 iIdx_key_,
/*OUT*/ int *result_
);
MCO_RET Record_I_Index_find (
mco_trans_h t,
uint4 iIdx_key_,
/*OUT*/ Record *handle_
);
该类中为非唯一哈希I_Series
生成的索引相关函数如下:
MCO_RET Record_I_Series_index_cursor (
mco_trans_h t,
/*OUT*/ mco_cursor_h c
);
MCO_RET Record_I_Series_compare (
mco_trans_h t,
mco_cursor_h c,
uint4 iSeries_key_,
/*OUT*/ int *result_
);
MCO_RET Record_I_Series_search (
mco_trans_h t,
/*INOUT*/ mco_cursor_h c,
uint4 iSeries_key_
);
注意
对于唯一哈希索引,生成的是精确匹配find()
函数;
对于非唯一哈希索引,生成的是search()
函数。
动态哈希表分配
可以启用动态哈希特性以便根据需要自动重新分配哈希表。这将避免:
- 如果估计的对象数量太少,则会出现长碰撞链;
- 在过于谨慎的大估计的情况下浪费内存。
在mco_db_params_t
结构中,传递给 mco_db_open_dev()
的dynamic_hash
参数(布尔值)决定是否启用动态哈希表扩展(默认是启用)。动态哈希启用适用于所有哈希数据库中的索引,以及哈希维护的表oid
和autoid
索引。
初始化哈希表分配使用两个值:
hash_load_factor
数据库模式中为该类指定的对象估计数量 hash_load_factor
参数(百分比值,默认100%),也是mco_db_params_t
结构的一部分。初始哈希表是估计的对象数 * 100 / hash_load_factor
。因此,
- 如果
hash_load_factor = 100
,则哈希表的初始大小为指定的估计对象数; - 如果
hash_load_factor = 50
,则哈希表的初始大小是估计对象数量的两倍; - 如果
hash_load_factor = 200
,则哈希表的初始大小是估计对象数量的一半。
当启用动态哈希时,hash_load_factor
参数用于确定何时重新分配哈希表。
例如:如果初始哈希表大小为1000,
hash_load_factor = 50
,那么当插入第501个对象时,哈希表将被扩展;hash_load_factor = 150
,则在插入第1501个对象时扩展哈希表。
应用程序可以通过将 dynamic_hash
参数设置为 false
来禁用动态哈希。这将导致更多的哈希冲突发生,因此带来性能损失。
游标和搜索
使用find()
函数执行具有唯一哈希索引的“精确匹配”搜索。对于非唯一哈希索引,使用search()
函数,一旦搜索操作找到了数据库对象,就使用标准游标定位函数mco_cursor_first()
、mco_cursor_last()
、mco_cursor_next()
和mco_cursor_prev()
来遍历结果集。 请参阅搜索页面了解实现细节。
OID索引
无论对象标识符(OID)是由外部源提供,还是通过对象作为对数据库中另一个对象的引用而检索到的,在 C 应用程序中,OID都可用于快速检索其标识的对象。在对象创建期间,SmartEDB 运行时会强制执行唯一性,并且可以使用 OID索引来在数据库中的类之间建立关系。
例如,要使用 OID在订单和执行之间建立关系,请参考以下代码片段:
struct OrderId
{
uint4 Id;
};
declare oid OrderId[1000]; // 1000 是预期值的数量(整个数据库范围内)
class Order
{
. . .
vector <ref> Executions;
. . .
};
class Execution
{
float quantity;
oid;
. . .
};
请注意,declare语句用于标识一个唯一的对象标识符,该标识符包含将与OID一起存储的对象的预期数量。运行时使用预期的条目数来计算索引的初始哈希表大小。
一旦声明了OID结构,就可以声明具有这个唯一标识符的类(如上面的Execution类)。运行时维护一个引用这些类的所有对象的内部索引。对象可以通过使用ref
数据类型的oid相互引用。
OID必须是用户定义的结构,即使它仅包含一个字段。每个OID值在整个数据库中必须唯一,并且在一个数据库模式中只能声明一次OID。此外,每个类只能使用一个OID语句。OID必须被分配一个在数据库范围内唯一的值。在此示例中,Order类包含一个可变长度数组(即向量),该数组包含对Execution类实例的OID引用。向量中的每个元素都是一个Execution对象实例的OID,可用于快速定位相应的Execution对象。
(关于创建oid引用的另一个示例,请参阅类关系页面。)
结构和功能
如果为数据库声明了一个oid,那么mcocomp模式编译器将生成一个与定义OID的结构相对应的C结构。例如,对于包含如下OID声明的模式:
declare database my_db;
struct structname
{
uint4 num_in;
};
declare oid structname[10000];
将生成以下定义和函数:
typedef struct my_db_oid__
{
uint4 num_in;
}
my_db_oid;
static const uint2 my_db_oid_size = sizeof(my_db_oid);
MCO_RET my_db_delete_object( /*IN*/ mco_trans_h t, /*IN*/ const my_db_oid * oid );
MCO_RET my_db_get_class_code( /*IN*/ mco_trans_h t, /*IN*/ const my_db_oid * oid,
/*OUT*/ uint2 *classcode );
对于给定的数据库,只能定义一个 OID,且该 OID 的所有值的唯一性将由 SmartEDB 运行时强制执行。_delete_object()
函数根据对象的 OID 删除对象。_get_class_code()
函数返回一个整数,该整数标识由指定 OID 值引用的对象的类。
对于包含OID 的类,将生成以下函数来创建对象、根据对象的oid定位对象以及提取对象的oid:
MCO_RET classname_new( /*IN*/ mco_trans_h t, /*IN*/ const my_db_oid *id,
/*OUT*/ classname *handle );
MCO_RET classname_oid_find( /*IN*/ mco_trans_h t, /*IN*/ const my_db_oid *id,
/*OUT*/ classname *handle );
MCO_RET classname_oid_get( /*IN*/ classname *handle, /*OUT*/ my_db_oid *id );
游标和搜索
OID 只能使用_oid_find()
函数进行“精确匹配”搜索。
请参阅搜索页面了解实现细节。
autoid索引
autoid是由SmartEDB运行时生成的保证唯一的值。对于C应用程序,autoid是在DDL类定义中声明的,带有该类的指定数量的估计对象。autoid索引可用于建立数据库中类之间的关系。
当一个类被声明为具有autoid时,模式编译器将为该类生成以下两个函数:
MCO_RET classname_autoid_find( /*IN*/ mco_trans_h t, /*IN*/ autoid_t id,
/*OUT*/ classname *handle );
MCO_RET classname_autoid_get( /*IN*/ classname *handle, /*OUT*/ autoid_t *id );
当创建带有autoid的类的对象时,运行时生成该对象的唯一autoid值,并将其插入到内部维护的散列索引中。可以使用为给定类生成的_autoid_get()
函数检索该值。autoid值,无论是存储在程序变量中还是作为引用存储在另一个数据库对象的字段中,都可以在_autoid_find()
函数中使用,以定位引用的对象,如下面的代码片段所示:
/* 模式定义代码片段 */
class referenced
{
...
autoid[4000];
...
};
class referencing
{
...
autoid_t refd_object;
...
};
Application code snippets:
autoid_t id;
mco_trans_start( db, MCO_READ_WRITE, MCO_TRANS_FOREGROUND, &t );
// 创建新对象,系统自动分配autoid
referenced_new( t, &refd_obj );
// 获取autoid值
referenced_autoid_get( &refd_obj, &id );
rc = referencing_new( t, &refg_obj ); // create new object
// 在其中存储引用的autoid值
referencing_refd_object_put( &refg_obj, id );
rc = mco_trans_commit( t );
/* 对象检索 */
rc = mco_trans_start( db, MCO_READ_ONLY, MCO_TRANS_FOREGROUND, &t );
/* 首先通过方法)找到一个“引用”对象*/
// 获取被引用对象的autoid
referencing_refd_object_get( &refg_obj, &id );
// 定位所引用的对象
rc = referenced_autoid_find( t, id, &refd_obj );
rc = mco_trans_commit( t );
这个代码片段展示了创建定义了autoid的类的新对象、检索系统分配的autoid值并将该值存储在引用它的类的对象的字段中的过程。稍后,从引用对象中提取autoid值,并通过它的_autoid_find()
函数来定位被引用的对象。
注意,autoid值的内部性质没有定义。autoid唯一定义的属性是它的大小为8字节,并且是唯一的。应用程序不应该依赖或期望autoid字段的数值。
游标和搜索
只有使用_autoid_find()
函数执行的“精确匹配”搜索才能对autoid进行搜索。
请参阅搜索页面了解实现细节。
用户定义索引
对于C应用程序,树和哈希索引可以在模式定义中声明为userdef
,在这种情况下,应用程序必须提供自定义比较函数,供数据库运行时在构建索引和查找期间使用。此外,对于散列索引,应用程序必须提供自定义散列函数。例如,考虑以下示例模式:
declare database mydb;
class Obj
{
unsigned<4> first_part1;
usigned<2> first_part2;
unsigned<4> second_part1;
signed<2> second_part2;
string data;
userdef hash <first_part1, first_part2> first[1000];
userdef tree <second_part1, second_part2> second;
};
为userdef hash
索引 first
生成以下函数:
MCO_RET Obj_first_index_cursor ( mco_trans_h t, /*OUT*/ mco_cursor_h c );
MCO_RET Obj_first_compare ( mco_trans_h t, mco_cursor_h c,
uint4 first_part1_key_, uint2 first_part2_key_, /*OUT*/ int *result_ );
MCO_RET Obj_first_find ( mco_trans_h t, uint4 first_part1_key_, uint2
irst_part2_key_, /*OUT*/ Obj *handle_);
为userdef tree
索引 second
生成以下函数:
MCO_RET Obj_second_index_cursor ( mco_trans_h t, /*OUT*/ mco_cursor_h c );
MCO_RET Obj_second_search ( mco_trans_h t, /*INOUT*/ mco_cursor_h c,
MCO_OPCODE op_, uint4 second_part1_key_, int2 second_part2_key_ );
MCO_RET Obj_second_compare ( mco_trans_h t, mco_cursor_h c,
uint4 second_part1_key_, int2 second_part2_key_, /*OUT*/ int *result_ );
MCO_RET Obj_second_locate ( mco_trans_h t, /*OUT*/ mco_cursor_h c,
Obj * handle);
对于树索引 second
,有必要实现两个函数:对象间比较函数和对象与键的比较函数。这些函数根据第一个参数是小于、等于还是大于第二个参数返回负值、零值或正值。
对于哈希索引 first
,除了比较函数之外,还需要实现 _hash_obj()
和 _hash_ext()
函数。如果第一个和第二个参数相等,则返回零,否则返回非零。函数原型由模式编译器生成,并放置在名为dbname_udf.c
的文件中(其中dbname
是declare语句中的数据库名称)。
例如,对于上面的示例模式,将生成以下mydb_udf.c
文件:
#include "mydb.h"
#include "mcowrap.h"
/*
* 用户自定义索引"first"的 API
*/
/* object-to-object user-defined compare function */
int2 Obj_first_compare_obj ( Obj * handle1, Obj * handle2 )
{
/* 在此处添加您的实现代码 */
return 0;
}
/* 对象到键的用户自定义比较函数 */
int2 Obj_first_compare_ext ( Obj * handle, void ** key )
{
/* 在此处添加您的实现代码 */
return 0;
}
/* 用户自定义对象哈希函数 */
uint4 Obj_first_hash_obj ( Obj * handle )
{
/* 在此处添加您的实现代码 */
return 0;
}
/* 用户自定义键哈希函数 */
uint4 Obj_first_hash_ext ( void ** key)
{
/* 在此处添加您的实现代码 */
return 0;
}
/*
* 用户自定义索引"second"的 AP
*/
/* 对象间的用户自定义比较函数 */
int2 Obj_second_compare_obj ( Obj * handle1, Obj * handle2 )
{
/* 在此处添加您的实现代码 */
return 0;
}
/* 对象到键的用户自定义比较函数 */
int2 Obj_second_compare_ext ( Obj * handle, void ** key )
{
/* 在此处添加您的实现代码 */
return 0;
}
如果文件dbname_udf.c
已经存在,DDL编译器将生成一个文件dbname_udf.c
。新建并显示警告。
如果用户定义的索引已经更改,那么.new
文件应该重命名为.c
文件。
自定义比较函数总是在事务的上下文中调用。因此,可以使用生成的_get()
函数访问对象字段。为了访问索引结构中的关键字段,需要在dbname.h
文件中生成补充的宏定义。对于上面的示例模式,这些将如下所示:
#define Obj_first_extkey_first_part1(k)
#define Obj_first_extkey_first_part2(k)
#define Obj_second_extkey_second_part1(ek)
#define Obj_second_extkey_second_part2(ek)
自定义比较和哈希函数的一个示例实现可以如下所示:
/* 对象间的用户自定义比较函数 */
int2 Obj_first_compare_obj ( Obj * handle1, Obj * handle2 )
{
uint4 o1_first_part1, o2_first _part1;
uint2 o1_first _part2, o2_first _part2;
Obj_first_part1_get( handle1, &o1_first_part1 );
Obj_first_part1_get( handle2, &o2_first_part1 );
if (o1_first_part1 != o2_first_part1)
return 1;
Obj_first_part2_get( handle1, &o1_first_part2 );
Obj_first_part2_get( handle2, &o2_first_part2 );
if (o1_first_part2 != o2_first_part2)
return 1;
return 0;
}
/* 对象到键的用户自定义比较函数 */
int2 Obj_first_compare_ext ( Obj * handle, void ** key )
{
uint4 o_first_part1;
uint2 o_first_part2;
Obj_first_part1_get( handle, &o_first_part1 );
if ( o_first_part1 != Obj_first_extkey_first_part1(key) )
return 1;
Obj_first_part2_get( handle, &o_first_part2 );
if ( o_first_part2 != Obj_first_extkey_first_part2(key) )
return 1;
return 0;
}
/* 用户自定义对象哈希函数 */
uint4 Obj_first_hash_obj ( Obj * handle )
{
uint4 o_first_part1;
uint2 o_first_part2;
uint4 hash;
Obj_first_part1_get( handle, &o_first_part1 );
Obj_first_part2_get( handle, &o_first_part2 );
hash = (o_first_part1*1000+o_first_part2) / 1000;
return hash;
}
/* 用户自定义键哈希函数 */
uint4 Obj_first_hash_ext ( void ** key)
{
uint4 hash;
hash = ( Obj_first_extkey_first_part1(key)*1000 +
Obj_first_extkey_first_part2(key)) / 1000;
return hash;
}
/*
* 用户自定义索引“second”的 API
*/
/* 对象间的用户自定义比较函数 */
int2 Obj_second_compare_obj ( Obj * handle1, Obj * handle2 )
{
uint4 o1_second_part1, o2_second _part1;
uint2 o1_second _part2, o2_second _part2;
Obj_second_part1_get( handle1, &o1_second_part1 );
Obj_second_part1_get( handle2, &o2_second_part1 );
Obj_second_part2_get( handle1, &o1_second_part2 );
Obj_second_part2_get( handle2, &o2_second_part2 );
if ( o1_second_part1 < o2_second_part1 )
return -1;
if ( o1_second_part1 > o2_second_part1 )
return 1;
if ( o1_second_part2 < o2_second_part2 )
return -1;
if ( o1_second_part2 > o2_second_part2 )
return 1;
return 0;
}
/* 对象到键的用户自定义比较函数 */
int2 Obj_second_compare_ext ( Obj * handle, void ** key )
{
uint4 o_second_part1;
uint2 o_second_part2;
Obj_second_part1_get( handle, &o_second_part1 );
Obj_second_part2_get( handle, &o_second_part2 );
if ( o_second_part1 < Obj_second_extkey_second_part1(key) )
return -1;
if ( o_second_part1 > Obj_second_extkey_second_part1(key) )
return 1;
if ( o_second_part2 < Obj_second_extkey_second_part2(key) )
return -1;
if ( o_second_part2 > Obj_second_extkey_second_part2(key) )
return 1;
return 0;
}
游标和搜索
如上所述,对于用户自定义哈希索引,仅生成一个 _find()
函数。但对于树形索引,会生成一个 _find()
函数和一个 _search()
函数。标准的游标定位函数 mco_cursor_first()
、mco_cursor_last()、mco_cursor_next()
和 mco_cursor_prev()
用于遍历结果集。
请参阅搜索页面了解实现细节。