用户定义函数
SmartESQL 支持用户定义函数(UDF),能够优化数据库操作的性能。UDF 用 C++ 编写,并编译成动态加载库,然后可在运行时由 C++ 应用程序加载。
例如,以下代码片段定义并注册了一个用户定义函数(UDF)mod()
:
static Value* mod(Value* a, Value* b)
{
if (a->isNull() || b->isNull())
{
return NULL;
}
return new IntValue(a->intValue() % b->intValue());
}
// f1 是 SqlFunctionDeclaration 类的一个实例。
// 构造函数将名为“mod”的用户定义函数的声明链接到由SmartESQL内部维护的UDF列表中:
static SqlFunctionDeclaration f1(
tpInt, // tpInt is the return value of the UDF
"mod", // the name of the function as we’ll use it in a query
(void*)mod, // the function pointer
2 // the number of arguments to the UDF
);
注册完成后,即可在该应用程序内的常规 SQL 选择语句中调用此用户定义函数。
例如,以下语句选择名为 code
的列的值能被 3 整除的记录:
QueryResult result(engine.executeQuery("select * from T where mod(code,3) = 0;"));
可加载的UDFs
用户定义函数(UDF)也可以编译成动态加载库。这些 UDF 可以由 SQL 引擎从任何嵌入式 SmartESQL 应用程序中加载。
可参阅SDK 示例 samples/xsql/data_loaders/CME/modcme/modcme.cpp。
以下片段是示例中用于创建 load_cme_trades`:
extern "C" {
CME_EXPORTS McoSql::Value *load_cme_trades(
McoSql::Runtime *runtime,
McoSql::Vector<McoSql::Value> *args
)
{
return load_security_rec<securityTrades, int, Trade> (
runtime->engine, runtime->trans, args, 11
);
}
}
此用户定义函数(UDF)调用了 load_security_rec()
函数,该函数执行了一组相对复杂的数据库操作:
template <class secAcc, class sKey, class secRec>
McoSql::Value *load_security_rec(McoSql::SqlEngine *engine,
McoSql::Transaction *currTrans, McoSql::Vector<McoSql::Value> *args, int ncolumns)
{
McoSql::Value *input = args->at(0);
McoSql::Value *book = args->at(1);
std::map<sKey, secAcc *> securities;
if (input->type() != McoSql::tpString)
{
throw McoSql::InvalidArgument("input - string sxpected");
}
char *fname = input->stringValue(currTrans->allocator)->cstr();
if (book->type() != McoSql::tpString)
{
throw McoSql::InvalidArgument("book - string sxpected");
}
char *Book = book->stringValue(currTrans->allocator)->cstr();
CsvReader r(currTrans->allocator, fname, ',', 2, ncolumns);
secRec s;
while (r.next(&s))
{
secAcc *st = NULL;
sKey key = s.key();
typename std::map<sKey, secAcc*>::iterator f = securities.find(key);
if (f == securities.end())
{
st = new secAcc(engine, currTrans, Book, key, BATCH_SIZE);
st->findRecord();
securities[key] = st;
}
else
{
st = f->second;
}
st->apply(&s);
}
typename std::map<sKey, secAcc *>::iterator it;
for (it = securities.begin(); it != securities.end(); it++)
{
it->second->store();
delete it->second;
}
return McoSql::IntValue::create(currTrans->allocator, securities.size());
}
要将此文件构建为动态加载库,必须指定如下一些链接器参数:
$(QUIET_PREFIX)$(LDCXX) $(SOLDFLAGS) $(LDOUTPUTF) $(MCO_BIN_SO)/libmcocme$(MCO_BUILDRT_SFX)$(MCO_CFG_SUFFIX_SO) $(OBJ_L) $(QUIET_SUFFIX)
请注意,此 makefile 片段使用了 SmartEDB SDK 构建系统中文件 include/header.mak
的多个宏。
请参阅完整的 makefile 样本 samples/xsql/data_loaders/CME/modcme/makefile。
加载与执行UDFs
要从外部动态加载库加载用户定义函数(UDF),嵌入式 SmartESQL 应用程序会调用 SQL 数据定义语言(DDL)命令 create function
,并指定文件和 UDF 名称。例如:
create function load_trades(fname string, book string) returns integer as 'mcocme', 'load_cme_trades';
在 xSQL 配置文件的 sql_statements
部分中可以包含 create function
命令,以使 xSQL 创建用户定义函数(UDF)并从动态加载库中加载其实现。或者,可以在 C++ 应用程序中打开 SQL 引擎时加载 UDF,方法是将 SqlOptimizerParamter 类的参数 preloadUDF
设置为 true
以启用预加载函数功能。然后将优化器参数添加到传递给 McoSqlEngine 方法 open()
的 McoSqlOpenParameters 中。例如:
McoSqlEngine engine;
McoSqlOpenParameters params;
SqlOptimizerParameters oParams;
oParams.preloadUDF = true;
...
params.optimizerParams = oParams;
engine.open(params);
...
一旦由 SQL 引擎加载,任何 SmartESQL 应用程序都可以通过 SQL 选择语句调用用户定义函数(UDF)。
例如,以下 Python 代码片段执行了上面定义的 load_trades
UDF:
is_shm = False
is_debug = True
is_disk = True
tmgr='mursiw'
exdb.init_runtime(is_disk, tmgr, is_shm, is_debug)
conn = exdb.connect("localhost:5001")
cursor = conn.cursor()
sql = '''SELECT load_trades('%s', '%s')''' % (fname, book)
cursor.execute(sql)
cursor.fetchall()
...