- prepared query
- bind parameters (input/output)
- bind column
- async mode for statements
- catalog functions
- luasql compatable module
- C API to create Lua object based on existed ODBC handle
- assert method rise error as object.
This works fine on Lua > 5.1 and on LuaJIT but on Lua 5.1 it results message as
lua.exe: (error object is not a string)
. But it allows wrap code inpcall
function and handle errors as object(with access to SQLCODE and SQLSTATE). To change this behavior set-DLODBC_ASSERT_TOSTRING
flag in compile time.
Insert multiple rows
odbc = require "odbc"
dbassert = odbc.assert
cnn = odbc.connect('EmptyDB')
cnn:setautocommit(false)
stmt = cnn:prepare("insert into test_table(f1, f2) values(?, ?)")
f1 = stmt:vbind_param_ulong(1)
for i = 1, 5 do
f1:set(i) -- just set data
stmt:bindnum(2, i) -- each time call SQLBindParameter
dbassert(stmt:execute())
end
dbassert( cnn:commit() )
Select resultset
odbc = require "odbc"
dbassert = odbc.assert
cnn = odbc.connect('EmptyDB')
stmt = cnn:execute('select f1, f2 from test_table order by 1')
stmt:foreach(function(f1, f2)
if f1 == 2 then return true end
end)
assert(stmt:closed()) -- foreach close cursor
assert(not stmt:destroyed()) -- statement valid
stmt:execute('select f1, f2 from test_table order by 1')
f1, f2 = stmt:fetch()
Input/Output parameters
odbc = require "odbc"
dbassert = odbc.assert
cnn = odbc.connect('EmptyDB')
stmt = dbassert(cnn:prepare("{?= call dba.fn_test(?)}"))
ret = stmt:vbind_param_ulong(1, ret, odbc.PARAM_OUTPUT)
val = stmt:vbind_param_char(2, "hello") -- default odbc.PARAM_INPUT
dbassert(stmt:execute())
print(ret:get())
stmt:reset()
stmt:vbind_col(1, ret)
dbassert(stmt:execute('select 321'))
stmt:vfetch()
print(ret:get())
Use async mode
local odbc = require "odbc"
local dbassert = odbc.assert
cnn = odbc.connect('EmptyDB')
stmt = cnn:prepare('select f1, f2 from test_table order by 1')
local f1 = stmt:vbind_col_ulong(1)
local f2 = stmt:vbind_col_ulong(2)
stmt:setasyncmode(true)
local COUNTER = 0
function async(stmt, f, ...)
while true do
local ok, err = f(stmt, ...)
if ok ~= 'timeout' then return ok, err end
COUNTER = COUNTER + 1
end
end
dbassert(async(stmt, stmt.execute))
while(async(stmt, stmt.vfetch))do
print(f1:get(),f2:get())
end
print("execute counter:", COUNTER)
Use C API
static void luaX_call_method(lua_State *L, const char *name, int nargs, int nresults){
int obj_index = -nargs - 1;
lua_getfield(L, obj_index, name);
lua_insert(L, obj_index - 1);
return lua_call(L, nargs + 1, nresults);
}
static int odbc_first_row_impl(lua_State *L){
SQLHSTMT *pstmt = (SQLHSTMT *)lua_touserdata(L, lua_upvalueindex(1));
lua_settop(L, 1); // sql
// wrap existed handle to lua object
lodbc_statement(L, *pstmt, 0); // sql, stmt
lua_insert(L, 1); lua_insert(L, 1); // stmt, sql
luaX_call_method(L, "execute", 1, 2); // [nil/stmt], [err/nil]
// here we should check error and ether execute return recordset
lua_pop(L, 1); //stmt
luaX_call_method(L, "fetch", 0, LUA_MULTRET);
return lua_gettop(L);
}
static int odbc_first_row(lua_State *L){
int res;
{ // this is may be cpp scope (we can use RAII)
// get existed handle from somewhere(e.g. prepared pool)
SQLHSTMT hstmt = ...
lua_pushlightuserdata(L, &hstmt);
lua_pushcclosure(L, odbc_first_row_impl, 1);
lua_insert(L, 1);
res = lua_pcall(L, lua_gettop(L)-1, LUA_MULTRET, 0);
// cleanup
}
if(res) return lua_error(L);
return lua_gettop(L);
}
And from Lua we can call this function just like ... = odbc.first_row("select ...")
Implementaion of dba API