Skip to content

helloj/lua-odbc

 
 

Repository files navigation

ODBC library for Lua 5.1/5.2

Licence Build Status Coverage Status

Supports

  • 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

Known issues

  • 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 in pcall function and handle errors as object(with access to SQLCODE and SQLSTATE). To change this behavior set -DLODBC_ASSERT_TOSTRING flag in compile time.

Usage

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 ...")

odbc.dba module

Implementaion of dba API

Bitdeli Badge

About

ODBC Library for lua

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages

  • C 54.0%
  • Lua 36.6%
  • CMake 7.6%
  • Other 1.8%