# HG changeset patch # User nrich@ii.net # Date 1228462875 0 # Node ID 98192b7d4e89a15d5e479cd64bcef9830f83c8d4 # Parent 10c8c6f0da14bc22d7d8b166b1d551f9a3bb70bd Add DB2 driver module. diff -r 10c8c6f0da14 -r 98192b7d4e89 DBI.lua --- a/DBI.lua Wed Dec 03 08:44:40 2008 +0000 +++ b/DBI.lua Fri Dec 05 07:41:15 2008 +0000 @@ -7,6 +7,7 @@ MySQL = 'dbdmysql', PostgreSQL = 'dbdpostgresql', SQLite3 = 'dbdsqlite3', + DB2 = 'dbddb2', } local string = require('string') diff -r 10c8c6f0da14 -r 98192b7d4e89 Makefile --- a/Makefile Wed Dec 03 08:44:40 2008 +0000 +++ b/Makefile Fri Dec 05 07:41:15 2008 +0000 @@ -1,5 +1,5 @@ CC=gcc -CFLAGS=-g -pedantic -O2 -Wall -shared -fpic -I /usr/include/lua5.1 -I /usr/include/mysql -I /usr/include/postgresql/ -I . +CFLAGS=-g -pedantic -O2 -Wall -shared -fpic -I /usr/include/lua5.1 -I /usr/include/mysql -I /usr/include/postgresql/ -I /opt/ibm/db2exc/V9.5/include/ -I . AR=ar rcu RANLIB=ranlib RM=rm -f @@ -8,16 +8,21 @@ MYSQL_LDFLAGS=$(COMMON_LDFLAGS) -lmysqlclient PSQL_LDFLAGS=$(COMMON_LDFLAGS) -lpq SQLITE3_LDFLAGS=$(COMMON_LDFLAGS) -lsqlite3 +DB2_LDFLAGS=$(COMMON_LDFLAGS) -L/opt/ibm/db2exc/V9.5/lib32 -ldb2 DBDMYSQL=dbdmysql.so DBDPSQL=dbdpostgresql.so DBDSQLITE3=dbdsqlite3.so +DBDDB2=dbddb2.so MYSQL_OBJS=build/dbd_mysql_main.o build/dbd_mysql_connection.o build/dbd_mysql_statement.o PSQL_OBJS=build/dbd_postgresql_main.o build/dbd_postgresql_connection.o build/dbd_postgresql_statement.o SQLITE3_OBJS=build/dbd_sqlite3_main.o build/dbd_sqlite3_connection.o build/dbd_sqlite3_statement.o +DB2_OBJS=build/dbd_db2_main.o build/dbd_db2_connection.o build/dbd_db2_statement.o -all: dbdmysql dbdpsql dbdsqlite3 +free: dbdmysql dbdpsql dbdsqlite3 + +all: dbdmysql dbdpsql dbdsqlite3 dbddb2 dbdmysql: $(MYSQL_OBJS) $(CC) $(CFLAGS) $(MYSQL_OBJS) -o $(DBDMYSQL) $(MYSQL_LDFLAGS) @@ -28,6 +33,9 @@ dbdsqlite3: $(SQLITE3_OBJS) $(CC) $(CFLAGS) $(SQLITE3_OBJS) -o $(DBDSQLITE3) $(SQLITE3_LDFLAGS) +dbddb2: $(DB2_OBJS) + $(CC) $(CFLAGS) $(DB2_OBJS) -o $(DBDDB2) $(DB2_LDFLAGS) + clean: $(RM) $(MYSQL_OBJS) $(PSQL_OBJS) $(SQLITE3_OBJS) $(DBDMYSQL) $(DBDPSQL) $(DBDSQLITE3) @@ -52,3 +60,10 @@ build/dbd_sqlite3_statement.o: dbd/sqlite3/statement.c dbd/sqlite3/dbd_sqlite3.h dbd/common.h $(CC) -c -o $@ $< $(CFLAGS) +build/dbd_db2_connection.o: dbd/db2/connection.c dbd/db2/dbd_db2.h dbd/common.h + $(CC) -c -o $@ $< $(CFLAGS) +build/dbd_db2_main.o: dbd/db2/main.c dbd/db2/dbd_db2.h dbd/common.h + $(CC) -c -o $@ $< $(CFLAGS) +build/dbd_db2_statement.o: dbd/db2/statement.c dbd/db2/dbd_db2.h dbd/common.h + $(CC) -c -o $@ $< $(CFLAGS) + diff -r 10c8c6f0da14 -r 98192b7d4e89 dbd/common.h --- a/dbd/common.h Wed Dec 03 08:44:40 2008 +0000 +++ b/dbd/common.h Fri Dec 05 07:41:15 2008 +0000 @@ -110,4 +110,5 @@ #define DBI_ERR_PREP_STATEMENT "Error preparing statement handle: %s" #define DBI_ERR_INVALID_PORT "Invalid port: %d" #define DBI_ERR_ALLOC_RESULT "Error allocating result set: %s" +#define DBI_ERR_DESC_RESULT "Error describing result set: %s" #define DBI_ERR_BINDING_TYPE_ERR "Unknown or unsupported type `%s'" diff -r 10c8c6f0da14 -r 98192b7d4e89 dbd/db2/connection.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbd/db2/connection.c Fri Dec 05 07:41:15 2008 +0000 @@ -0,0 +1,251 @@ +#include "dbd_db2.h" + +int dbd_db2_statement_create(lua_State *L, connection_t *conn, const char *sql_query); + +static int commit(connection_t *conn) { + SQLRETURN rc = SQL_SUCCESS; + + rc = SQLEndTran(SQL_HANDLE_DBC, conn->db2, SQL_COMMIT); + + return rc != SQL_SUCCESS; +} + +static int rollback(connection_t *conn) { + SQLRETURN rc = SQL_SUCCESS; + + rc = SQLEndTran(SQL_HANDLE_DBC, conn->db2, SQL_ROLLBACK); + + return rc != SQL_SUCCESS; +} + + +/* + * connection = DBD.DB2.New(dbname, user, password, host, port) + */ +static int connection_new(lua_State *L) { + int n = lua_gettop(L); + connection_t *conn = NULL; + SQLRETURN rc = SQL_SUCCESS; + + const char *user = NULL; + const char *password = NULL; + const char *db = NULL; + + SQLCHAR message[SQL_MAX_MESSAGE_LENGTH + 1]; + SQLCHAR sqlstate[SQL_SQLSTATE_SIZE + 1]; + SQLINTEGER sqlcode; + SQLSMALLINT length; + + /* db, user, password, host, port */ + switch(n) { + case 5: + case 4: + case 3: + if (lua_isnil(L, 3) == 0) + password = luaL_checkstring(L, 3); + case 2: + if (lua_isnil(L, 2) == 0) + user = luaL_checkstring(L, 2); + case 1: + /* + * db is the only mandatory parameter + */ + db = luaL_checkstring(L, 1); + } + + conn = (connection_t *)lua_newuserdata(L, sizeof(connection_t)); + + /* allocate an environment handle */ + rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &conn->env); + if (rc != SQL_SUCCESS) { + lua_pushnil(L); + lua_pushfstring(L, DBI_ERR_CONNECTION_FAILED, "Cannot allocate environment handle"); + return 2; + } + + /* set attribute to enable application to run as ODBC 3.0 application */ + rc = SQLSetEnvAttr(conn->env, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0); + if (rc != SQL_SUCCESS) { + lua_pushnil(L); + lua_pushfstring(L, DBI_ERR_CONNECTION_FAILED, "Cannot set ODBC version attribute"); + return 2; + } + + /* allocate a database connection handle */ + rc = SQLAllocHandle(SQL_HANDLE_DBC, conn->env, &conn->db2); + if (rc != SQL_SUCCESS) { + lua_pushnil(L); + lua_pushfstring(L, DBI_ERR_CONNECTION_FAILED, "Cannot allocate database handle"); + return 2; + } + + /* set AUTOCOMMIT off */ + rc = SQLSetConnectAttr(conn->db2, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)SQL_AUTOCOMMIT_OFF, SQL_NTS); + if (rc != SQL_SUCCESS) { + lua_pushnil(L); + lua_pushfstring(L, DBI_ERR_CONNECTION_FAILED, "Cannot turn off autocommit"); + return 2; + } + + /* connect to the database */ + rc = SQLConnect(conn->db2, (SQLCHAR *)db, SQL_NTS, (SQLCHAR *)user, SQL_NTS, (SQLCHAR *)password, SQL_NTS); + 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_CONNECTION_FAILED, message); + return 2; + } + + luaL_getmetatable(L, DBD_DB2_CONNECTION); + lua_setmetatable(L, -2); + + return 1; +} + +/* + * success = connection:autocommit(on) + */ +static int connection_autocommit(lua_State *L) { + connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_DB2_CONNECTION); + int on = lua_toboolean(L, 2); + int err = 0; + + if (conn->db2) { + int onval = on ? SQL_AUTOCOMMIT_ON : SQL_AUTOCOMMIT_OFF; + + SQLRETURN rc = SQLSetConnectAttr(conn->db2, SQL_ATTR_AUTOCOMMIT, (SQLPOINTER)onval, SQL_NTS); + + err = rc != SQL_SUCCESS; + } + + lua_pushboolean(L, !err); + return 1; +} + +/* + * success = connection:close() + */ +static int connection_close(lua_State *L) { + connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_DB2_CONNECTION); + int disconnect = 0; + + if (conn->db2) { + SQLRETURN rc = SQL_SUCCESS; + + rollback(conn); + + /* disconnect from the database */ + rc = SQLDisconnect(conn->db2); + + /* free connection handle */ + rc = SQLFreeHandle(SQL_HANDLE_DBC, conn->db2); + + /* free environment handle */ + rc = SQLFreeHandle(SQL_HANDLE_ENV, conn->env); + + conn->db2 = 0; + } + + lua_pushboolean(L, disconnect); + return 1; +} + +/* + * success = connection:commit() + */ +static int connection_commit(lua_State *L) { + connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_DB2_CONNECTION); + int err = 1; + + if (conn->db2) { + err = commit(conn); + } + + lua_pushboolean(L, !err); + return 1; +} + +/* + * ok = connection:ping() + */ +static int connection_ping(lua_State *L) { + connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_DB2_CONNECTION); + int ok = 0; + + if (conn->db2) { + ok = 1; + } + + lua_pushboolean(L, ok); + return 1; +} + +/* + * statement = connection:prepare(sql_string) + */ +static int connection_prepare(lua_State *L) { + connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_DB2_CONNECTION); + + if (conn->db2) { + return dbd_db2_statement_create(L, conn, luaL_checkstring(L, 2)); + } + + lua_pushnil(L); + lua_pushstring(L, DBI_ERR_DB_UNAVAILABLE); + return 2; +} + +/* + * success = connection:rollback() + */ +static int connection_rollback(lua_State *L) { + connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_DB2_CONNECTION); + int err = 1; + + if (conn->db2) { + err = rollback(conn); + } + + lua_pushboolean(L, !err); + return 1; +} + +/* + * __gc + */ +static int connection_gc(lua_State *L) { + /* always close the connection */ + connection_close(L); + + return 0; +} + +int dbd_db2_connection(lua_State *L) { + static const luaL_Reg connection_methods[] = { + {"autocommit", connection_autocommit}, + {"close", connection_close}, + {"commit", connection_commit}, + {"ping", connection_ping}, + {"prepare", connection_prepare}, + {"rollback", connection_rollback}, + {NULL, NULL} + }; + + static const luaL_Reg connection_class_methods[] = { + {"New", connection_new}, + {NULL, NULL} + }; + + luaL_newmetatable(L, DBD_DB2_CONNECTION); + luaL_register(L, 0, connection_methods); + lua_pushvalue(L,-1); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, connection_gc); + lua_setfield(L, -2, "__gc"); + + luaL_register(L, DBD_DB2_CONNECTION, connection_class_methods); + + return 1; +} diff -r 10c8c6f0da14 -r 98192b7d4e89 dbd/db2/dbd_db2.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbd/db2/dbd_db2.h Fri Dec 05 07:41:15 2008 +0000 @@ -0,0 +1,49 @@ +#include +#include +#include +#include + +#define DBD_DB2_CONNECTION "DBD.DB2.Connection" +#define DBD_DB2_STATEMENT "DBD.DB2.Statement" + +/* + * result set metadata + */ + +typedef struct _resultset { + SQLSMALLINT name_len; + SQLSMALLINT type; + SQLUINTEGER size; + SQLSMALLINT scale; + SQLCHAR name[32]; +} resultset_t; + +/* + * bind parameters + */ +typedef struct _bindparams { + SQLCHAR *buffer; + SQLINTEGER len; + SQLINTEGER buffer_len; +} bindparams_t; + +/* + * connection object implentation + */ +typedef struct _connection { + SQLHANDLE env; + SQLHANDLE db2; +} connection_t; + +/* + * statement object implementation + */ +typedef struct _statement { + resultset_t * resultset; + bindparams_t * bind; + SQLSMALLINT num_result_columns; /* variable for SQLNumResultCols */ + + SQLHANDLE stmt; + SQLHANDLE db2; +} statement_t; + diff -r 10c8c6f0da14 -r 98192b7d4e89 dbd/db2/main.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbd/db2/main.c Fri Dec 05 07:41:15 2008 +0000 @@ -0,0 +1,15 @@ +#include "dbd_db2.h" + +int dbd_db2_connection(lua_State *L); +int dbd_db2_statement(lua_State *L); + +/* + * library entry point + */ +int luaopen_dbddb2(lua_State *L) { + dbd_db2_connection(L); + dbd_db2_statement(L); + + return 1; +} + diff -r 10c8c6f0da14 -r 98192b7d4e89 dbd/db2/statement.c --- /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; +} diff -r 10c8c6f0da14 -r 98192b7d4e89 dbd/mysql/connection.c --- a/dbd/mysql/connection.c Wed Dec 03 08:44:40 2008 +0000 +++ b/dbd/mysql/connection.c Fri Dec 05 07:41:15 2008 +0000 @@ -34,8 +34,10 @@ if (lua_isnil(L, 2) == 0) user = luaL_checkstring(L, 2); case 1: - if (lua_isnil(L, 1) == 0) - db = luaL_checkstring(L, 1); + /* + * db is the only mandatory parameter + */ + db = luaL_checkstring(L, 1); } conn = (connection_t *)lua_newuserdata(L, sizeof(connection_t)); diff -r 10c8c6f0da14 -r 98192b7d4e89 dbd/mysql/statement.c --- a/dbd/mysql/statement.c Wed Dec 03 08:44:40 2008 +0000 +++ b/dbd/mysql/statement.c Fri Dec 05 07:41:15 2008 +0000 @@ -54,6 +54,9 @@ statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT); int num_bind_params = n - 1; int expected_params; + + unsigned char *buffer = NULL; + int offset = 0; MYSQL_BIND *bind = NULL; MYSQL_RES *metadata = NULL; @@ -101,8 +104,11 @@ break; case LUA_TBOOLEAN: - boolean = (int *)malloc(sizeof(int)); + buffer = realloc(buffer, offset + sizeof(int)); + boolean = (int *)buffer + offset; + offset += sizeof(int); *boolean = lua_toboolean(L, p); + bind[i].buffer_type = MYSQL_TYPE_LONG; bind[i].is_null = (my_bool*)0; bind[i].buffer = (char *)boolean; @@ -114,7 +120,9 @@ * num needs to be it's own * memory here */ - num = (double *)malloc(sizeof(double)); + buffer = realloc(buffer, offset + sizeof(double)); + num = (double *)buffer + offset; + offset += sizeof(double); *num = lua_tonumber(L, p); bind[i].buffer_type = MYSQL_TYPE_DOUBLE; @@ -124,7 +132,9 @@ break; case LUA_TSTRING: - str_len = malloc(sizeof(size_t)); + buffer = realloc(buffer, offset + sizeof(size_t)); + str_len = (size_t *)buffer + offset; + offset += sizeof(size_t); str = lua_tolstring(L, p, str_len); bind[i].buffer_type = MYSQL_TYPE_STRING; @@ -154,28 +164,13 @@ metadata = mysql_stmt_result_metadata(statement->stmt); cleanup: - if (bind) { - int i; + /* + * free the buffer with a resize to 0 + */ + realloc(buffer, 0); - for (i = 0; i < num_bind_params; i++) { - /* - * Free the memory associated with - * the allocation of double and string - * bind params. If the interface are - * extended with other types they - * will need to be added here - */ - if (bind[i].buffer_type == MYSQL_TYPE_DOUBLE || bind[i].buffer_type == MYSQL_TYPE_LONG) { - if (bind[i].buffer) - free(bind[i].buffer); - } else if (bind[i].buffer_type == MYSQL_TYPE_STRING) { - if (bind[i].length) - free(bind[i].length); - } - } - + if (bind) free(bind); - } if (error_message) { lua_pushboolean(L, 0); diff -r 10c8c6f0da14 -r 98192b7d4e89 dbd/postgresql/connection.c --- a/dbd/postgresql/connection.c Wed Dec 03 08:44:40 2008 +0000 +++ b/dbd/postgresql/connection.c Fri Dec 05 07:41:15 2008 +0000 @@ -75,8 +75,10 @@ if (lua_isnil(L, 2) == 0) user = luaL_checkstring(L, 2); case 1: - if (lua_isnil(L, 1) == 0) - db = luaL_checkstring(L, 1); + /* + * db is the only mandatory parameter + */ + db = luaL_checkstring(L, 1); } conn = (connection_t *)lua_newuserdata(L, sizeof(connection_t)); diff -r 10c8c6f0da14 -r 98192b7d4e89 dbd/sqlite3/connection.c --- a/dbd/sqlite3/connection.c Wed Dec 03 08:44:40 2008 +0000 +++ b/dbd/sqlite3/connection.c Fri Dec 05 07:41:15 2008 +0000 @@ -35,8 +35,10 @@ /* db */ switch(n) { default: - if (lua_isnil(L, 1) == 0) - db = luaL_checkstring(L, 1); + /* + * db is the only mandatory parameter + */ + db = luaL_checkstring(L, 1); } conn = (connection_t *)lua_newuserdata(L, sizeof(connection_t));