Files
sysbench/sysbench/sb_lua.c
2017-01-14 01:29:57 +03:00

1137 lines
25 KiB
C

/* Copyright (C) 2006 MySQL AB
Copyright (C) 2006-2017 Alexey Kopytov <akopytov@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "sb_lua.h"
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "db_driver.h"
#include "sb_rand.h"
#include "sb_thread.h"
#include "ck_pr.h"
/* Auto-generated headers */
#include "lua/internal/sysbench.lua.h"
#include "lua/internal/sysbench.sql.lua.h"
#define EVENT_FUNC "event"
#define PREPARE_FUNC "prepare"
#define CLEANUP_FUNC "cleanup"
#define HELP_FUNC "help"
#define THREAD_INIT_FUNC "thread_init"
#define THREAD_DONE_FUNC "thread_done"
#define THREAD_RUN_FUNC "thread_run"
#define INIT_FUNC "init"
#define DONE_FUNC "done"
/* Macros to call Lua functions */
#define CALL_ERROR(L, name) \
do { \
const char *err = lua_tostring(L, -1); \
log_text(LOG_FATAL, "failed to execute function `%s': %s", \
name, err ? err : "(null)"); \
} while (0)
#define CHECK_CONNECTION(L, ctxt) \
do { \
if (ctxt->con == NULL) \
luaL_error(L, "Uninitialized database connection"); \
} while(0);
/* Interpreter context */
typedef struct {
int thread_id; /* sysbench thread id */
db_conn_t *con; /* Database connection */
} sb_lua_ctxt_t;
typedef struct {
int id;
db_bind_type_t type;
void *buf;
unsigned long buflen;
char is_null;
} sb_lua_bind_t;
typedef struct {
db_result_t *ptr;
} sb_lua_db_rs_t;
typedef struct {
db_stmt_t *ptr;
sb_lua_bind_t *params;
unsigned int nparams;
sb_lua_bind_t *results;
unsigned int nresults;
int param_ref;
int result_ref;
sb_lua_db_rs_t *rs;
} sb_lua_db_stmt_t;
/* Lua interpreter states */
static lua_State **states;
/* Event counter */
static unsigned int nevents;
static sb_test_t sbtest;
static const char *sb_lua_script_path;
/* Lua test operations */
static int sb_lua_op_init(void);
static int sb_lua_op_done(void);
static sb_event_t sb_lua_next_event(int);
static int sb_lua_op_thread_init(int);
static int sb_lua_op_thread_run(int);
static int sb_lua_op_thread_done(int);
static void sb_lua_op_print_stats(sb_stat_t type);
static sb_operations_t lua_ops = {
.init = sb_lua_op_init,
.next_event = &sb_lua_next_event,
.done = sb_lua_op_done
};
/* Main (global) interpreter state */
static lua_State *gstate;
/* Database driver */
static TLS db_driver_t *db_driver;
/* Variable with unique address to store per-state data */
static const char sb_lua_ctxt_key = 0;
/* Lua test commands */
static int sb_lua_cmd_prepare(void);
static int sb_lua_cmd_cleanup(void);
static int sb_lua_cmd_help(void);
/* Initialize interpreter state */
static lua_State *sb_lua_new_state(int);
/* Close interpretet state */
static int sb_lua_close_state(lua_State *);
/* Exported C functions for legacy Lua API */
static int sb_lua_db_connect(lua_State *);
static int sb_lua_db_disconnect(lua_State *);
static int sb_lua_db_query(lua_State *);
static int sb_lua_db_bulk_insert_init(lua_State *);
static int sb_lua_db_bulk_insert_next(lua_State *);
static int sb_lua_db_bulk_insert_done(lua_State *);
static int sb_lua_db_prepare(lua_State *);
static int sb_lua_db_bind_param(lua_State *);
static int sb_lua_db_bind_result(lua_State *);
static int sb_lua_db_execute(lua_State *);
static int sb_lua_db_close(lua_State *);
static int sb_lua_db_store_results(lua_State *);
static int sb_lua_db_free_results(lua_State *);
static int sb_lua_more_events(lua_State *);
static int sb_lua_event_start(lua_State *);
static int sb_lua_event_stop(lua_State *);
/* Get a per-state interpreter context */
static sb_lua_ctxt_t *sb_lua_get_context(lua_State *);
/* Set a per-state interpreter context */
void sb_lua_set_context(lua_State *, sb_lua_ctxt_t *);
unsigned int sb_lua_table_size(lua_State *, int);
/* Load a specified Lua script */
sb_test_t *sb_load_lua(const char *testname)
{
#ifdef DATA_PATH
setenv("LUA_PATH", DATA_PATH LUA_DIRSEP "?.lua", 0);
#endif
sb_lua_script_path = testname;
/* Initialize global interpreter state */
gstate = sb_lua_new_state(-1);
if (gstate == NULL)
goto error;
/* Test commands */
lua_getglobal(gstate, PREPARE_FUNC);
if (!lua_isnil(gstate, -1))
sbtest.cmds.prepare = &sb_lua_cmd_prepare;
lua_pop(gstate, 1);
lua_getglobal(gstate, CLEANUP_FUNC);
if (!lua_isnil(gstate, -1))
sbtest.cmds.cleanup = &sb_lua_cmd_cleanup;
lua_getglobal(gstate, HELP_FUNC);
if (!lua_isnil(gstate, -1) && lua_isfunction(gstate, -1))
sbtest.cmds.help = &sb_lua_cmd_help;
/* Test operations */
sbtest.ops = lua_ops;
sbtest.ops.thread_init = &sb_lua_op_thread_init;
sbtest.ops.thread_done = &sb_lua_op_thread_done;
lua_getglobal(gstate, THREAD_RUN_FUNC);
if (!lua_isnil(gstate, -1))
sbtest.ops.thread_run = &sb_lua_op_thread_run;
sbtest.ops.print_stats = &sb_lua_op_print_stats;
/* Allocate per-thread interpreters array */
states = (lua_State **)calloc(sb_globals.num_threads, sizeof(lua_State *));
if (states == NULL)
goto error;
return &sbtest;
error:
sb_lua_close_state(gstate);
if (states != NULL)
free(states);
return NULL;
}
/* Initialize Lua script */
int sb_lua_op_init(void)
{
lua_getglobal(gstate, INIT_FUNC);
if (!lua_isnil(gstate, -1))
{
if (lua_pcall(gstate, 0, 0, 0))
{
CALL_ERROR(gstate, INIT_FUNC);
return 1;
}
}
return 0;
}
sb_event_t sb_lua_next_event(int thread_id)
{
sb_event_t req;
(void) thread_id; /* unused */
if (sb_globals.max_requests > 0 &&
ck_pr_faa_uint(&nevents, 1) >= sb_globals.max_requests)
{
req.type = SB_REQ_TYPE_NULL;
return req;
}
req.type = SB_REQ_TYPE_SCRIPT;
return req;
}
int sb_lua_op_thread_init(int thread_id)
{
lua_State * L;
L = sb_lua_new_state(thread_id);
if (L == NULL)
return 1;
states[thread_id] = L;
lua_getglobal(L, THREAD_INIT_FUNC);
if (!lua_isnil(L, -1))
{
lua_pushnumber(L, thread_id);
if (lua_pcall(L, 1, 1, 0))
{
CALL_ERROR(L, THREAD_INIT_FUNC);
return 1;
}
}
return 0;
}
int sb_lua_op_thread_run(int thread_id)
{
lua_State * const L = states[thread_id];
lua_getglobal(L, THREAD_RUN_FUNC);
lua_pushnumber(L, thread_id);
if (lua_pcall(L, 1, 1, 0))
{
CALL_ERROR(L, THREAD_RUN_FUNC);
return 1;
}
return 0;
}
int sb_lua_op_thread_done(int thread_id)
{
lua_State * const L = states[thread_id];
int rc = 0;
lua_getglobal(L, THREAD_DONE_FUNC);
if (!lua_isnil(L, -1))
{
lua_pushnumber(L, thread_id);
if (lua_pcall(L, 1, 1, 0))
{
CALL_ERROR(L, THREAD_DONE_FUNC);
rc = 1;
}
}
sb_lua_close_state(L);
return rc;
}
void sb_lua_op_print_stats(sb_stat_t type)
{
db_print_stats(type);
}
int sb_lua_op_done(void)
{
if (states != NULL)
free(states);
lua_getglobal(gstate, DONE_FUNC);
if (!lua_isnil(gstate, -1))
{
if (lua_pcall(gstate, 0, 0, 0))
{
CALL_ERROR(gstate, DONE_FUNC);
return 1;
}
}
return 0;
}
/* Allocate and initialize new interpreter state */
lua_State *sb_lua_new_state(int thread_id)
{
lua_State *L;
sb_lua_ctxt_t *ctxt;
sb_list_item_t *pos;
option_t *opt;
char *tmp;
L = luaL_newstate();
luaL_openlibs(L);
/* Export all global options */
pos = sb_options_enum_start();
while ((pos = sb_options_enum_next(pos, &opt)) != NULL)
{
switch (opt->type)
{
case SB_ARG_TYPE_FLAG:
lua_pushboolean(L, sb_opt_to_flag(opt));
break;
case SB_ARG_TYPE_INT:
lua_pushnumber(L, sb_opt_to_int(opt));
break;
case SB_ARG_TYPE_FLOAT:
lua_pushnumber(L, sb_opt_to_float(opt));
break;
case SB_ARG_TYPE_SIZE:
lua_pushnumber(L, sb_opt_to_size(opt));
break;
case SB_ARG_TYPE_STRING:
tmp = sb_opt_to_string(opt);
lua_pushstring(L, tmp ? tmp : "");
break;
case SB_ARG_TYPE_LIST:
/*FIXME: should be exported as tables */
lua_pushnil(L);
break;
case SB_ARG_TYPE_FILE:
/* FIXME: no need to export anything */
lua_pushnil(L);
break;
default:
log_text(LOG_WARNING, "Global option '%s' will not be exported, because"
" the type is unknown", opt->name);
lua_pushnil(L);
break;
}
lua_setglobal(L, opt->name);
}
#define SB_LUA_VAR(luaname, cname) \
do { \
lua_pushstring(L, luaname); \
lua_pushnumber(L, cname); \
lua_settable(L, -3); \
} while (0)
#define SB_LUA_FUNC(luaname, cname) \
do { \
lua_pushstring(L, luaname); \
lua_pushcfunction(L, cname); \
lua_settable(L, -3); \
} while (0)
/* Export variables into per-state 'sysbench' table */
lua_newtable(L);
/* sysbench.tid */
if (thread_id >= 0)
SB_LUA_VAR("tid", thread_id);
/* Export functions into per-state 'sysbench' table */
SB_LUA_FUNC("more_events", sb_lua_more_events);
SB_LUA_FUNC("event_start", sb_lua_event_start);
SB_LUA_FUNC("event_stop", sb_lua_event_stop);
/* Export functions into per-state 'sysbench.db' table */
lua_pushstring(L, "db");
lua_newtable(L);
SB_LUA_FUNC("connect", sb_lua_db_connect);
SB_LUA_FUNC("disconnect", sb_lua_db_disconnect);
SB_LUA_FUNC("query", sb_lua_db_query);
SB_LUA_FUNC("bulk_insert_init", sb_lua_db_bulk_insert_init);
SB_LUA_FUNC("bulk_insert_next", sb_lua_db_bulk_insert_next);
SB_LUA_FUNC("bulk_insert_done", sb_lua_db_bulk_insert_done);
SB_LUA_FUNC("prepare", sb_lua_db_prepare);
SB_LUA_FUNC("bind_param", sb_lua_db_bind_param);
SB_LUA_FUNC("bind_result", sb_lua_db_bind_result);
SB_LUA_FUNC("execute", sb_lua_db_execute);
SB_LUA_FUNC("close", sb_lua_db_close);
SB_LUA_FUNC("store_results", sb_lua_db_store_results);
SB_LUA_FUNC("free_results", sb_lua_db_free_results);
SB_LUA_VAR("DB_ERROR_NONE", DB_ERROR_NONE);
SB_LUA_VAR("DB_ERROR_RESTART_TRANSACTION", DB_ERROR_IGNORABLE);
SB_LUA_VAR("DB_ERROR_FAILED", DB_ERROR_FATAL);
#undef SB_LUA_VAR
#undef SB_LUA_FUNC
lua_settable(L, -3); /* sysbench.db */
lua_setglobal(L, "sysbench");
luaL_newmetatable(L, "sysbench.stmt");
luaL_newmetatable(L, "sysbench.rs");
/* Pre-load internal modules */
if (luaL_loadbuffer(L, (const char *) sysbench_lua, sysbench_lua_len,
"sysbench.lua"))
{
log_text(LOG_FATAL, "failed to load internal module sysbench.lua: %s",
lua_tostring(L, -1));
return NULL;
}
lua_pushstring(L, "sysbench");
lua_call(L, 1, 0);
if (luaL_loadbuffer(L, (const char *) sysbench_sql_lua, sysbench_sql_lua_len,
"sysbench.sql.lua"))
{
log_text(LOG_FATAL, "failed to load internal module sysbench.sql.lua: %s",
lua_tostring(L, -1));
return NULL;
}
lua_pushstring(L, "sysbench.sql");
lua_call(L, 1, 0);
int rc;
if ((rc = luaL_loadfile(L, sb_lua_script_path)))
{
if (rc != LUA_ERRFILE)
{
lua_error(L);
return NULL;
}
#ifdef DATA_PATH
/* first location failed - look in DATA_PATH */
char p[PATH_MAX + 1];
strncpy(p, DATA_PATH LUA_DIRSEP, sizeof(p));
strncat(p, sb_lua_script_path, sizeof(p)-strlen(p)-1);
if (!strrchr(sb_lua_script_path, '.'))
{
/* add .lua extension if there isn't one */
strncat(p, ".lua", sizeof(p)-strlen(p)-1);
}
if (luaL_loadfile(L, p))
{
lua_error(L);
return NULL;
}
#else
lua_error(L);
return NULL;
#endif
}
if (lua_pcall(L, 0, 0, 0))
{
lua_error(L);
return NULL;
}
/* Create new L context */
ctxt = (sb_lua_ctxt_t *)calloc(1, sizeof(sb_lua_ctxt_t));
if (ctxt == NULL)
return NULL;
ctxt->thread_id = thread_id;
sb_lua_set_context(L, ctxt);
return L;
}
/* Close interpreter state */
int sb_lua_close_state(lua_State *state)
{
if (state != NULL)
lua_close(state);
if (db_driver != NULL)
{
db_destroy(db_driver);
db_driver = NULL;
}
return 0;
}
/* Prepare command */
int sb_lua_cmd_prepare(void)
{
lua_getglobal(gstate, PREPARE_FUNC);
if (lua_pcall(gstate, 0, 1, 0) != 0)
{
log_text(LOG_FATAL, "failed to execute function `"PREPARE_FUNC"': %s",
lua_tostring(gstate, -1));
return 1;
}
return 0;
}
/* Cleanup command */
int sb_lua_cmd_cleanup(void)
{
lua_getglobal(gstate, CLEANUP_FUNC);
if (lua_pcall(gstate, 0, 1, 0) != 0)
{
log_text(LOG_FATAL, "failed to execute function `"CLEANUP_FUNC"': %s",
lua_tostring(gstate, -1));
return 1;
}
return 0;
}
/* Help command */
int sb_lua_cmd_help(void)
{
lua_getglobal(gstate, HELP_FUNC);
if (lua_pcall(gstate, 0, 1, 0) != 0)
{
log_text(LOG_FATAL, "failed to execute function `"HELP_FUNC"': %s",
lua_tostring(gstate, -1));
return 1;
}
return 0;
}
int sb_lua_db_connect(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
ctxt = sb_lua_get_context(L);
db_driver = db_create(NULL);
if (db_driver == NULL)
luaL_error(L, "DB initialization failed");
lua_pushstring(L, db_driver->sname);
lua_setglobal(L, "db_driver");
ctxt->con = db_connection_create(db_driver);
if (ctxt->con == NULL)
luaL_error(L, "Failed to connect to the database");
db_set_thread(ctxt->con, ctxt->thread_id);
return 0;
}
int sb_lua_db_disconnect(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
ctxt = sb_lua_get_context(L);
if (ctxt->con)
{
db_connection_close(ctxt->con);
db_connection_free(ctxt->con);
}
ctxt->con = NULL;
return 0;
}
int sb_lua_db_query(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
const char *query;
db_result_t *rs;
size_t len;
ctxt = sb_lua_get_context(L);
if (ctxt->con == NULL)
sb_lua_db_connect(L);
query = luaL_checklstring(L, 1, &len);
rs = db_query(ctxt->con, query, len);
if (rs == NULL)
{
lua_pushnumber(L, ctxt->con->db_errno);
lua_error(L);
}
db_free_results(rs);
return 0;
}
int sb_lua_db_bulk_insert_init(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
const char *query;
size_t len;
ctxt = sb_lua_get_context(L);
if (ctxt->con == NULL)
sb_lua_db_connect(L);
query = luaL_checklstring(L, 1, &len);
if (db_bulk_insert_init(ctxt->con, query, len))
luaL_error(L, "db_bulk_insert_init() failed");
return 0;
}
int sb_lua_db_bulk_insert_next(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
const char *query;
size_t len;
ctxt = sb_lua_get_context(L);
CHECK_CONNECTION(L, ctxt);
query = luaL_checklstring(L, 1, &len);
if (db_bulk_insert_next(ctxt->con, query, len))
luaL_error(L, "db_bulk_insert_next() failed");
return 0;
}
int sb_lua_db_bulk_insert_done(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
ctxt = sb_lua_get_context(L);
CHECK_CONNECTION(L, ctxt);
db_bulk_insert_done(ctxt->con);
return 0;
}
int sb_lua_db_prepare(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
sb_lua_db_stmt_t *stmt;
const char *query;
size_t len;
ctxt = sb_lua_get_context(L);
if (ctxt->con == NULL)
sb_lua_db_connect(L);
query = luaL_checklstring(L, 1, &len);
stmt = (sb_lua_db_stmt_t *)lua_newuserdata(L, sizeof(sb_lua_db_stmt_t));
luaL_getmetatable(L, "sysbench.stmt");
lua_setmetatable(L, -2);
memset(stmt, 0, sizeof(sb_lua_db_stmt_t));
stmt->ptr = db_prepare(ctxt->con, query, len);
if (stmt->ptr == NULL)
luaL_error(L, "db_prepare() failed");
stmt->param_ref = LUA_REFNIL;
return 1;
}
int sb_lua_db_bind_param(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
sb_lua_db_stmt_t *stmt;
unsigned int i, n;
db_bind_t *binds;
char needs_rebind = 0;
ctxt = sb_lua_get_context(L);
CHECK_CONNECTION(L, ctxt);
stmt = (sb_lua_db_stmt_t *)luaL_checkudata(L, 1, "sysbench.stmt");
luaL_argcheck(L, stmt != NULL, 1, "prepared statement expected");
if (!lua_istable(L, 2))
luaL_error(L, "table was expected");
/* Get table size */
n = sb_lua_table_size(L, 2);
if (!n)
luaL_error(L, "table is empty");
binds = (db_bind_t *)calloc(n, sizeof(db_bind_t));
stmt->params = (sb_lua_bind_t *)calloc(n, sizeof(sb_lua_bind_t));
if (binds == NULL || stmt->params == NULL)
luaL_error(L, "memory allocation failure");
lua_pushnil(L);
for (i = 0; i < n; i++)
{
lua_next(L, 2);
switch(lua_type(L, -1))
{
case LUA_TNUMBER:
stmt->params[i].buf = malloc(sizeof(int));
stmt->params[i].id = luaL_checknumber(L, -2);
binds[i].type = DB_TYPE_INT;
binds[i].buffer = stmt->params[i].buf;
break;
case LUA_TSTRING:
stmt->params[i].id = luaL_checknumber(L, -2);
stmt->params[i].buflen = 0;
binds[i].type = DB_TYPE_CHAR;
needs_rebind = 1;
break;
default:
lua_pushfstring(L, "Unsupported variable type: %s",
lua_typename(L, lua_type(L, -1)));
goto error;
}
binds[i].is_null = &stmt->params[i].is_null;
stmt->params[i].type = binds[i].type;
lua_pop(L, 1);
}
if (!needs_rebind && db_bind_param(stmt->ptr, binds, n))
goto error;
stmt->nparams = n;
/* Create reference for the params table */
lua_pushvalue(L, 2);
stmt->param_ref = luaL_ref(L, LUA_REGISTRYINDEX);
free(binds);
return 0;
error:
free(binds);
lua_error(L);
return 0;
}
int sb_lua_db_bind_result(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
sb_lua_db_stmt_t *stmt;
unsigned int i, n;
db_bind_t *binds;
char needs_rebind = 0;
ctxt = sb_lua_get_context(L);
CHECK_CONNECTION(L, ctxt);
stmt = (sb_lua_db_stmt_t *)luaL_checkudata(L, 1, "sysbench.stmt");
luaL_argcheck(L, stmt != NULL, 1, "prepared statement expected");
if (!lua_istable(L, 2))
luaL_error(L, "table was expected");
/* Get table size */
n = sb_lua_table_size(L, 2);
if (!n)
luaL_error(L, "table is empty");
binds = (db_bind_t *)calloc(n, sizeof(db_bind_t));
stmt->results = (sb_lua_bind_t *)calloc(n, sizeof(sb_lua_bind_t));
if (binds == NULL || stmt->results == NULL)
luaL_error(L, "memory allocation failure");
lua_pushnil(L);
for (i = 0; i < n; i++)
{
lua_next(L, 2);
switch(lua_type(L, -1))
{
case LUA_TNUMBER:
stmt->results[i].buf = malloc(sizeof(int));
stmt->results[i].id = luaL_checknumber(L, -2);
binds[i].type = DB_TYPE_BIGINT;
binds[i].buffer = stmt->results[i].buf;
break;
case LUA_TSTRING:
stmt->results[i].id = luaL_checknumber(L, -2);
binds[i].type = DB_TYPE_CHAR;
needs_rebind = 1;
break;
default:
lua_pushfstring(L, "Unsupported variable type: %s",
lua_typename(L, lua_type(L, -1)));
goto error;
}
binds[i].is_null = &stmt->results[i].is_null;
stmt->results[i].type = binds[i].type;
lua_pop(L, 1);
}
if (!needs_rebind && db_bind_result(stmt->ptr, binds, n))
goto error;
stmt->nresults = n;
/* Create reference for the params table */
lua_pushvalue(L, 2);
stmt->result_ref = luaL_ref(L, LUA_REGISTRYINDEX);
// free(binds);
return 0;
error:
free(binds);
lua_error(L);
return 0;
}
int sb_lua_db_execute(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
sb_lua_db_stmt_t *stmt;
db_result_t *ptr;
sb_lua_db_rs_t *rs;
unsigned int i;
char needs_rebind = 0;
db_bind_t *binds;
size_t length;
const char *str;
sb_lua_bind_t *param;
ctxt = sb_lua_get_context(L);
CHECK_CONNECTION(L, ctxt);
stmt = (sb_lua_db_stmt_t *)luaL_checkudata(L, 1, "sysbench.stmt");
luaL_argcheck(L, stmt != NULL, 1, "prepared statement expected");
/* Get params table */
lua_rawgeti(L, LUA_REGISTRYINDEX, stmt->param_ref);
if (!lua_isnil(L, -1) && !lua_istable(L, -1))
luaL_error(L, "table expected");
for (i = 0; i < stmt->nparams; lua_pop(L, 1), i++)
{
param = stmt->params + i;
lua_pushnumber(L, param->id);
lua_gettable(L, -2);
if (lua_isnil(L, -1))
{
param->is_null = 1;
continue;
}
param->is_null = 0;
switch (param->type)
{
case DB_TYPE_INT:
*((int *)param->buf) = luaL_checknumber(L, -1);
break;
case DB_TYPE_CHAR:
str = luaL_checkstring(L, -1);
length = lua_objlen(L, -1);
if (length > param->buflen)
{
param->buf = realloc(param->buf, length);
needs_rebind = 1;
}
strncpy(param->buf, str, length);
param->buflen = length;
break;
default:
luaL_error(L, "Unsupported variable type: %s",
lua_typename(L, lua_type(L, -1)));
}
}
/* Rebind if needed */
if (needs_rebind)
{
binds = (db_bind_t *)calloc(stmt->nparams, sizeof(db_bind_t));
if (binds == NULL)
luaL_error(L, "Memory allocation failure");
for (i = 0; i < stmt->nparams; i++)
{
param = stmt->params + i;
binds[i].type = param->type;
binds[i].is_null = &param->is_null;
if (*binds[i].is_null != 0)
continue;
switch (param->type)
{
case DB_TYPE_INT:
binds[i].buffer = param->buf;
break;
case DB_TYPE_CHAR:
binds[i].buffer = param->buf;
binds[i].data_len = &stmt->params[i].buflen;
binds[i].is_null = 0;
break;
default:
luaL_error(L, "Unsupported variable type");
}
}
if (db_bind_param(stmt->ptr, binds, stmt->nparams))
luaL_error(L, "db_bind_param() failed");
free(binds);
}
ptr = db_execute(stmt->ptr);
if (ptr == NULL)
{
stmt->rs = NULL;
lua_pushnumber(L, ctxt->con->db_errno);
lua_error(L);
}
else
{
rs = (sb_lua_db_rs_t *)lua_newuserdata(L, sizeof(sb_lua_db_rs_t));
rs->ptr = ptr;
luaL_getmetatable(L, "sysbench.rs");
lua_setmetatable(L, -2);
stmt->rs = rs;
}
return 1;
}
int sb_lua_db_close(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
sb_lua_db_stmt_t *stmt;
unsigned int i;
ctxt = sb_lua_get_context(L);
CHECK_CONNECTION(L, ctxt);
stmt = (sb_lua_db_stmt_t *)luaL_checkudata(L, 1, "sysbench.stmt");
luaL_argcheck(L, stmt != NULL, 1, "prepared statement expected");
for (i = 0; i < stmt->nparams; i++)
{
if (stmt->params[i].buf != NULL)
free(stmt->params[i].buf);
}
free(stmt->params);
stmt->params = NULL;
luaL_unref(L, LUA_REGISTRYINDEX, stmt->param_ref);
luaL_unref(L, LUA_REGISTRYINDEX, stmt->result_ref);
db_close(stmt->ptr);
return 0;
}
int sb_lua_db_store_results(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
sb_lua_db_rs_t *rs;
ctxt = sb_lua_get_context(L);
CHECK_CONNECTION(L, ctxt);
rs = (sb_lua_db_rs_t *)luaL_checkudata(L, 1, "sysbench.rs");
luaL_argcheck(L, rs != NULL, 1, "result set expected");
/* noop as db_store_results() is now performed automatically by db_query() */
return 0;
}
int sb_lua_db_free_results(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
sb_lua_db_rs_t *rs;
ctxt = sb_lua_get_context(L);
CHECK_CONNECTION(L, ctxt);
rs = (sb_lua_db_rs_t *)luaL_checkudata(L, 1, "sysbench.rs");
luaL_argcheck(L, rs != NULL, 1, "result set expected");
db_free_results(rs->ptr);
rs->ptr = NULL;
return 0;
}
/* Get a per-state interpreter context */
sb_lua_ctxt_t *sb_lua_get_context(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
lua_pushlightuserdata(L, (void *)&sb_lua_ctxt_key);
lua_gettable(L, LUA_REGISTRYINDEX);
ctxt = (sb_lua_ctxt_t *)lua_touserdata(L, -1);
if (ctxt == NULL)
luaL_error(L, "Attempt to access database driver before it is initialized. "
"Check your script for syntax errors");
return ctxt;
}
/* Set a per-state interpreter context */
void sb_lua_set_context(lua_State *L, sb_lua_ctxt_t *ctxt)
{
lua_pushlightuserdata(L, (void *)&sb_lua_ctxt_key);
lua_pushlightuserdata(L, (void *)ctxt);
lua_settable(L, LUA_REGISTRYINDEX);
}
unsigned int sb_lua_table_size(lua_State *L, int index)
{
unsigned int i;
lua_pushnil(L);
for (i = 0; lua_next(L, index); i++)
{
lua_pop(L, 1);
}
return i;
}
/* Check if there are more events to execute */
int sb_lua_more_events(lua_State *L)
{
sb_event_t e;
sb_lua_ctxt_t *ctxt;
ctxt = sb_lua_get_context(L);
e = sb_next_event(&sbtest, ctxt->thread_id);
lua_pushboolean(L, e.type == SB_REQ_TYPE_SCRIPT);
return 1;
}
/* Exported wrapper for sb_event_start() */
int sb_lua_event_start(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
ctxt = sb_lua_get_context(L);
sb_event_start(ctxt->thread_id);
return 0;
}
/* Exported wrapper for sb_event_stop() */
int sb_lua_event_stop(lua_State *L)
{
sb_lua_ctxt_t *ctxt;
ctxt = sb_lua_get_context(L);
sb_event_stop(ctxt->thread_id);
return 0;
}