#通过编译LuaSQL调用ODBC连接Access以及给OpenResty(LuaJIT)编写简单的C扩展示例
**目录**
[TOC]
[前一篇文章](LuaJIT_FFI_ODBC_Connect_Access "前一篇文章")写了一个通过`ffi`调用`odbc`连接`mdb`的一个库,当然,这个过程是很痛苦的……
于是我开始自我怀疑,`LuaJIT`到底能不能使用`Lua`的一些库?`LuaJIT`按理说跟`Lua5.1`版本是很接近的,那么接口的扩展方式也是一样的……
但是当我把`LuaSQL`的`odbc.dll`拷进去,并且根据示例执行`require("luasql.odbc")`时,却提示“`找不到指定的模块`”……
这使我一度怀疑,`LuaJIT`不能使用`Lua`的一些库……当然,现在要拨乱反正,证明这个想法是错误的……
在此之前,我们先编写一个简单的`Hello world`的`C扩展`给`LuaJIT`调用试试,这也是我的探索之路,相信在这个过程中,大家也会渐渐的明白到底问题在哪儿……
##给OpenResty(LuaJIT)编写简单的C扩展示例
我们的扩展只有一个文件,那就是`hello.c`文件
```C
//gcc lua51.dll hello.c -shared -Llua -Iinclude\luajit-2.1 -o hello.dll
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
static const char* dohello(const char* src)
{
printf(src);
return "I'm OK!";
}
static int l_hello(lua_State* lua)
{
const char *src = NULL;
src = luaL_checkstring(lua, 1); //出栈获取源字符串
const char * des = dohello(src); //something
lua_pushstring(lua, des); //压栈返回给lua
return 1; //告诉lua返回了一个变量
}
//映射表,"doHello"为lua中的函数名,l_hello为真正C中的函数地址
static const struct luaL_Reg libhello[] = {
{"doHello", l_hello},
{NULL, NULL},
};
//模块注册函数
int luaopen_hello(lua_State* lua)
{
//注册本模块中所有的功能函数,hello为模块名,libhello数组存储所有函数的映射关系
luaL_register(lua, "hello", libhello);
return 1;
}
```
我是通过`MinGW`进行编译的,把`hello.c`文件放到`OpenResty`目录中去,调用`gcc`命令进行编译:
```Shell
gcc lua51.dll hello.c -shared -Llua -Iinclude\luajit-2.1 -o hello.dll
```
编译成功后得到`hello.dll`,用`luaJIT`简单的测试一下:
```
> hello = require("hello")
> r = hello.doHello("run~\n")
run~
> print(r)
I'm OK!
```
看来很成功~可惜我一开始做的时候不是这么顺利的……
在不断的调试过程中我发现了一点:
**luaopen_后面跟着的字符串与luaL_register函数调用的第二个参数一定要一样!同时与dll的文件名也一定要一样!切记!切记!**
这点很重要,是我经过多次的失败才发现的……这种要求大约也是我当年在`java`下编译`HelloWorld`一下午不成功的原因……当时都要哭了……
由于受这一点的启发,我似乎明白了我一直以来总是调用`luasql_odbc`不成功的原因……**名称不一致**……
下面介绍编译并调用`luaSQL`的正确姿势……
##通过编译luaSQL调用ODBC连接Access
首先,去[下载`luaSQL`的源码](https://github.com/keplerproject/luasql "下载`luaSQL`的源码")……
然后将下载下来的文件放到`OpenResty`的目录下的`luasql目录`中去,这么放的目的仅仅是为了引用`include文件`以及`dll`方便……
直接`make odbc`是不行的,我看了一下它的`makefile`,里面很多乱七八糟的变量需要修改,而且编译后的文件名默认是`odbc.so`……
仔细看看它引出的函数就知道,这个文件名是不能被`LuaJIT`正确加载的,需要改成`luasql_odbc`……
我为了描述省事,直接写了两条`gcc命令`来编译,这两条命令只会编译`luaSQL_ODBC`:
```Shell
gcc -I../include/luajit-2.1 -DLUASQL_VERSION_NUMBER='"2.3.4"' -c src/luasql.c -o luasql.o
gcc -I../include/luajit-2.1 -DLUASQL_VERSION_NUMBER='"2.3.4"' ../lua51.dll src/ls_odbc.c -o luasql_odbc.dll -shared luasql.o -lodbc32
```
这两条命令将生成两个文件,`luasql.o`和`luasql_odbc.dll`……
把`luasql_odbc.dll`放到`OpenResty`的目录中,写个`lua脚本`来调用一下试试吧……
还是用上次的那个`test.mdb`做示例吧……测试脚本`testLuaSqlOdbc.lua`的内容为:
```Lua
local ENV = require("luasql_odbc")
local env = ENV.odbc();
local conn,err = env:connect("excuteSQL");
if not conn then
print(err);
env:close();
return -3;
end
function excuteSQL(sql)
local n = conn:execute(sql);
if not n then
print("执行SQL失败");
return -4;
else
print("影响行数:"..tostring(n));
end
return 0;
end
function itorator(cur)
local row = {};
return function()
return cur:fetch(row,'a');
end
end
function excuteQuery(sql)
local cur = conn:execute(sql);
if not cur then
print("执行SQL失败");
return -5;
else
local row = nil;
for row in itorator(cur) do
local out = {};
for k,v in pairs(row) do
table.insert(out,k.."="..v)
end
print(table.concat(out,","));
end
cur:close();
end
return 0;
end
excuteSQL("insert into [test]([name],[value]) values('testluasqlodbc',123)");
excuteQuery("select * from [test]");
conn:close();
env:close();
return 0;
```
更多的`luaSQL_odbc`的调用接口在源码的`doc`里面看吧,这里只是简单的测试一下我们刚刚编译的东西能不能用……输出结果:
```
影响行数:1
value=101,name=testOOO,id=1
value=23,name=tesooo0,id=2
value=100,name=testodbcConn1,id=3
value=2333,name=tesodbcConntp1,id=4
value=123,name=testluasqlodbc,id=5
```
应该是没什么大问题的……然而,`luaSQL_odbc`所实现的接口其实也不多,也是刚刚够用而已……
然而……让我踏入的大坑则是`LuaSQL`生成的动态链接库的文件名称不一致……
这致使我一直以为`LuaSQL`连接`ODBC`在`OpenResty`环境下是一个不能用的库……
于是也直接引发了我为`LuaJIT`写了一个`ffi绑定库`……
故事很曲折却是因为绕了一个大圈……分享出来,以免网友们重蹈覆辙……