--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbd/db2/statement.c Fri Dec 05 07:41:15 2008 +0000 @@ -0,0 +1,431 @@ +#include "dbd_db2.h" + +#define MAX_COLUMNS 255 + +#ifndef max +#define max(a,b) (a > b ? a : b) +#endif + +static const char *strlower(char *in) { + char *s = in; + + while(*s) { + *s= (*s <= 'Z' && *s >= 'A') ? (*s - 'A') + 'a' : *s; + s++; + } + + return in; +} + +static lua_push_type_t db2_to_lua_push(unsigned int db2_type, int len) { + lua_push_type_t lua_type; + + if (len == SQL_NULL_DATA) + return LUA_PUSH_NIL; + + switch(db2_type) { + case SQL_SMALLINT: + case SQL_INTEGER: + lua_type = LUA_PUSH_INTEGER; + break; + case SQL_DECIMAL: + lua_type = LUA_PUSH_NUMBER; + break; + default: + lua_type = LUA_PUSH_STRING; + } + + return lua_type; +} + +/* + * success = statement:close() + */ +static int statement_close(lua_State *L) { + statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_DB2_STATEMENT); + SQLRETURN rc = SQL_SUCCESS; + + if (statement->stmt) { + rc = SQLFreeHandle(SQL_HANDLE_STMT, statement->stmt); + + if (statement->resultset) + free(statement->resultset); + + if (statement->bind) { + int i; + + for (i = 0; i < statement->num_result_columns; i++) { + free(statement->bind[i].buffer); + } + + free(statement->bind); + } + + statement->num_result_columns = 0; + } + + return 0; +} + +/* + * success = statement:execute(...) + */ +static int statement_execute(lua_State *L) { + int n = lua_gettop(L); + statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_DB2_STATEMENT); + int p; + int i; + int errflag = 0; + const char *errstr = NULL; + SQLRETURN rc = SQL_SUCCESS; + unsigned char *buffer = NULL; + int offset = 0; + resultset_t *resultset = NULL; + bindparams_t *bind; /* variable to read the results */ + + SQLCHAR message[SQL_MAX_MESSAGE_LENGTH + 1]; + SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1]; + SQLINTEGER sqlcode; + SQLSMALLINT length; + + if (!statement->stmt) { + lua_pushboolean(L, 0); + lua_pushstring(L, DBI_ERR_EXECUTE_INVALID); + return 2; + } + + for (p = 2; p <= n; p++) { + int i = p - 1; + int type = lua_type(L, p); + char err[64]; + const char *str = NULL; + size_t len = 0; + double *num; + int *boolean; + const static SQLLEN nullvalue = SQL_NULL_DATA; + + switch(type) { + case LUA_TNIL: + rc = SQLBindParameter(statement->stmt, i, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, (SQLPOINTER)0, 0, (SQLPOINTER)&nullvalue); + errflag = rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO; + break; + case LUA_TNUMBER: + buffer = realloc(buffer, offset + sizeof(double)); + num = (double *)buffer + offset; + *num = lua_tonumber(L, p); + offset += sizeof(double); + rc = SQLBindParameter(statement->stmt, i, SQL_PARAM_INPUT, SQL_C_DOUBLE, SQL_DECIMAL, 10, 0, (SQLPOINTER)num, 0, NULL); + errflag = rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO; + break; + case LUA_TSTRING: + str = lua_tolstring(L, p, &len); + rc = SQLBindParameter(statement->stmt, i, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR, 0, 0, (SQLPOINTER)str, len, NULL); + errflag = rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO; + break; + case LUA_TBOOLEAN: + buffer = realloc(buffer, offset + sizeof(int)); + boolean = (int *)buffer + offset; + *boolean = lua_toboolean(L, p); + offset += sizeof(int); + rc = SQLBindParameter(statement->stmt, i, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER, 0, 0, (SQLPOINTER)boolean, len, NULL); + errflag = rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO; + break; + default: + /* + * Unknown/unsupported value type + */ + errflag = 1; + snprintf(err, sizeof(err)-1, DBI_ERR_BINDING_TYPE_ERR, lua_typename(L, type)); + errstr = err; + } + + if (errflag) + break; + } + + if (errflag) { + realloc(buffer, 0); + lua_pushboolean(L, 0); + + if (errstr) { + lua_pushfstring(L, DBI_ERR_BINDING_PARAMS, errstr); + } else { + SQLGetDiagRec(SQL_HANDLE_STMT, statement->stmt, 1, sqlstate, &sqlcode, message, SQL_MAX_MESSAGE_LENGTH + 1, &length); + + lua_pushfstring(L, DBI_ERR_BINDING_PARAMS, message); + } + + return 2; + } + + rc = SQLExecute(statement->stmt); + if (rc != SQL_SUCCESS) { + SQLGetDiagRec(SQL_HANDLE_STMT, statement->stmt, 1, sqlstate, &sqlcode, message, SQL_MAX_MESSAGE_LENGTH + 1, &length); + + lua_pushnil(L); + lua_pushfstring(L, DBI_ERR_PREP_STATEMENT, message); + return 2; + } + + /* + * identify the number of output columns + */ + rc = SQLNumResultCols(statement->stmt, &statement->num_result_columns); + + if (statement->num_result_columns > 0) { + resultset = (resultset_t *)malloc(sizeof(resultset_t) * statement->num_result_columns); + memset(resultset, 0, sizeof(resultset_t) * statement->num_result_columns); + + bind = (bindparams_t *)malloc(sizeof(bindparams_t) * statement->num_result_columns); + memset(bind, 0, sizeof(bindparams_t) * statement->num_result_columns); + + for (i = 0; i < statement->num_result_columns; i++) { + /* + * return a set of attributes for a column + */ + rc = SQLDescribeCol(statement->stmt, + (SQLSMALLINT)(i + 1), + resultset[i].name, + sizeof(resultset[i].name), + &resultset[i].name_len, + &resultset[i].type, + &resultset[i].size, + &resultset[i].scale, + NULL); + + if (rc != SQL_SUCCESS) { + SQLGetDiagRec(SQL_HANDLE_STMT, statement->stmt, 1, sqlstate, &sqlcode, message, SQL_MAX_MESSAGE_LENGTH + 1, &length); + + lua_pushnil(L); + lua_pushfstring(L, DBI_ERR_DESC_RESULT, message); + return 2; + } + + bind[i].buffer_len = resultset[i].size+1; + + /* + *allocate memory to bind a column + */ + bind[i].buffer = (SQLCHAR *)malloc((int)bind[i].buffer_len); + + rc = SQLBindCol(statement->stmt, + (SQLSMALLINT)(i + 1), + SQL_C_CHAR, + bind[i].buffer, + bind[i].buffer_len, + &bind[i].len); + + if (rc != SQL_SUCCESS) { + SQLGetDiagRec(SQL_HANDLE_STMT, statement->stmt, 1, sqlstate, &sqlcode, message, SQL_MAX_MESSAGE_LENGTH + 1, &length); + + lua_pushnil(L); + lua_pushfstring(L, DBI_ERR_ALLOC_RESULT, message); + return 2; + } + } + + statement->resultset = resultset; + statement->bind = bind; + } + + /* + * free the buffer with a resize to 0 + */ + realloc(buffer, 0); + + lua_pushboolean(L, 1); + return 1; +} + +/* + * must be called after an execute + */ +static int statement_fetch_impl(lua_State *L, statement_t *statement, int named_columns) { + int i; + int d; + + SQLRETURN rc = SQL_SUCCESS; + + if (!statement->resultset || !statement->bind) { + lua_pushnil(L); + return 1; + } + + /* fetch each row, and display */ + rc = SQLFetch(statement->stmt); + if (rc == SQL_NO_DATA_FOUND) { + lua_pushnil(L); + return 1; + } + + d = 1; + lua_newtable(L); + for (i = 0; i < statement->num_result_columns; i++) { + lua_push_type_t lua_push = db2_to_lua_push(statement->resultset[i].type, statement->bind[i].len); + const char *name = strlower((char *)statement->resultset[i].name); + double val; + char *value = (char *)statement->bind[i].buffer; + + switch (lua_push) { + case LUA_PUSH_NIL: + if (named_columns) { + LUA_PUSH_ATTRIB_NIL(name); + } else { + LUA_PUSH_ARRAY_NIL(d); + } + break; + case LUA_PUSH_INTEGER: + if (named_columns) { + LUA_PUSH_ATTRIB_INT(name, atoi(value)); + } else { + LUA_PUSH_ARRAY_INT(d, atoi(value)); + } + break; + case LUA_PUSH_NUMBER: + val = strtod(value, NULL); + + if (named_columns) { + LUA_PUSH_ATTRIB_FLOAT(name, val); + } else { + LUA_PUSH_ARRAY_FLOAT(d, val); + } + break; + case LUA_PUSH_BOOLEAN: + if (named_columns) { + LUA_PUSH_ATTRIB_BOOL(name, atoi(value)); + } else { + LUA_PUSH_ARRAY_BOOL(d, atoi(value)); + } + break; + case LUA_PUSH_STRING: + if (named_columns) { + LUA_PUSH_ATTRIB_STRING(name, value); + } else { + LUA_PUSH_ARRAY_STRING(d, value); + } + break; + default: + luaL_error(L, DBI_ERR_UNKNOWN_PUSH); + } + } + + return 1; +} + + +static int next_iterator(lua_State *L) { + statement_t *statement = (statement_t *)luaL_checkudata(L, lua_upvalueindex(1), DBD_DB2_STATEMENT); + int named_columns = lua_toboolean(L, lua_upvalueindex(2)); + + return statement_fetch_impl(L, statement, named_columns); +} + +/* + * table = statement:fetch(named_indexes) + */ +static int statement_fetch(lua_State *L) { + statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_DB2_STATEMENT); + int named_columns = lua_toboolean(L, 2); + + return statement_fetch_impl(L, statement, named_columns); +} + +/* + * iterfunc = statement:rows(named_indexes) + */ +static int statement_rows(lua_State *L) { + if (lua_gettop(L) == 1) { + lua_pushvalue(L, 1); + lua_pushboolean(L, 0); + } else { + lua_pushvalue(L, 1); + lua_pushboolean(L, lua_toboolean(L, 2)); + } + + lua_pushcclosure(L, next_iterator, 2); + return 1; +} + +/* + * __gc + */ +static int statement_gc(lua_State *L) { + /* always free the handle */ + statement_close(L); + + return 0; +} + +int dbd_db2_statement_create(lua_State *L, connection_t *conn, const char *sql_query) { + SQLRETURN rc = SQL_SUCCESS; + statement_t *statement = NULL; + SQLHANDLE stmt; + + SQLCHAR message[SQL_MAX_MESSAGE_LENGTH + 1]; + SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1]; + SQLINTEGER sqlcode; + SQLSMALLINT length; + + rc = SQLAllocHandle(SQL_HANDLE_STMT, conn->db2, &stmt); + if (rc != SQL_SUCCESS) { + SQLGetDiagRec(SQL_HANDLE_DBC, conn->db2, 1, sqlstate, &sqlcode, message, SQL_MAX_MESSAGE_LENGTH + 1, &length); + + lua_pushnil(L); + lua_pushfstring(L, DBI_ERR_ALLOC_STATEMENT, message); + return 2; + } + + /* + * turn off deferred prepare + * statements will be sent to the server at prepare timr, + * and therefor we can catch errors then rather + * than at execute time + */ + rc = SQLSetStmtAttr(stmt,SQL_ATTR_DEFERRED_PREPARE,(SQLPOINTER)SQL_DEFERRED_PREPARE_OFF,0); + + rc = SQLPrepare(stmt, (SQLCHAR *)sql_query, SQL_NTS); + if (rc != SQL_SUCCESS) { + SQLGetDiagRec(SQL_HANDLE_STMT, stmt, 1, sqlstate, &sqlcode, message, SQL_MAX_MESSAGE_LENGTH + 1, &length); + + lua_pushnil(L); + lua_pushfstring(L, DBI_ERR_PREP_STATEMENT, message); + return 2; + } + + statement = (statement_t *)lua_newuserdata(L, sizeof(statement_t)); + statement->stmt = stmt; + statement->db2 = conn->db2; + statement->resultset = NULL; + statement->bind = NULL; + + luaL_getmetatable(L, DBD_DB2_STATEMENT); + lua_setmetatable(L, -2); + + return 1; +} + +int dbd_db2_statement(lua_State *L) { + static const luaL_Reg statement_methods[] = { + {"close", statement_close}, + {"execute", statement_execute}, + {"fetch", statement_fetch}, + {"rows", statement_rows}, + {NULL, NULL} + }; + + static const luaL_Reg statement_class_methods[] = { + {NULL, NULL} + }; + + luaL_newmetatable(L, DBD_DB2_STATEMENT); + luaL_register(L, 0, statement_methods); + lua_pushvalue(L,-1); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, statement_gc); + lua_setfield(L, -2, "__gc"); + + luaL_register(L, DBD_DB2_STATEMENT, statement_class_methods); + + return 1; +}