Add DB2 driver module.

Fri, 05 Dec 2008 07:41:15 +0000

author
nrich@ii.net
date
Fri, 05 Dec 2008 07:41:15 +0000
changeset 14
98192b7d4e89
parent 13
10c8c6f0da14
child 15
c51b16479373

Add DB2 driver module.

DBI.lua file | annotate | diff | comparison | revisions
Makefile file | annotate | diff | comparison | revisions
dbd/common.h file | annotate | diff | comparison | revisions
dbd/db2/connection.c file | annotate | diff | comparison | revisions
dbd/db2/dbd_db2.h file | annotate | diff | comparison | revisions
dbd/db2/main.c file | annotate | diff | comparison | revisions
dbd/db2/statement.c file | annotate | diff | comparison | revisions
dbd/mysql/connection.c file | annotate | diff | comparison | revisions
dbd/mysql/statement.c file | annotate | diff | comparison | revisions
dbd/postgresql/connection.c file | annotate | diff | comparison | revisions
dbd/sqlite3/connection.c file | annotate | diff | comparison | revisions
--- 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')
--- 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)
+
--- 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'"
--- /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;    
+}
--- /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 <sqlcli1.h>
+#include <sqlutil.h>
+#include <sqlenv.h>
+#include <dbd/common.h>
+
+#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;
+
--- /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;
+}
+
--- /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;    
+}
--- 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));
--- 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);
--- 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));
--- 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));

mercurial