Initial import.

Sun, 23 Nov 2008 01:29:09 +0000

author
nrich@ii.net
date
Sun, 23 Nov 2008 01:29:09 +0000
changeset 1
408291a6eb3e
parent 0
4ff31a4ea1fb
child 2
c4f02fc67e5a

Initial import.

DBI.lua file | annotate | diff | comparison | revisions
Makefile file | annotate | diff | comparison | revisions
dbd/common.h file | annotate | diff | comparison | revisions
dbd/mysql/connection.c file | annotate | diff | comparison | revisions
dbd/mysql/dbd_mysql.h file | annotate | diff | comparison | revisions
dbd/mysql/main.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/postgresql/dbd_postgresql.h file | annotate | diff | comparison | revisions
dbd/postgresql/main.c file | annotate | diff | comparison | revisions
dbd/postgresql/statement.c file | annotate | diff | comparison | revisions
dbd/sqlite3/connection.c file | annotate | diff | comparison | revisions
dbd/sqlite3/dbd_sqlite3.h file | annotate | diff | comparison | revisions
dbd/sqlite3/main.c file | annotate | diff | comparison | revisions
dbd/sqlite3/statement.c file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/DBI.lua	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,50 @@
+#!/usr/bin/lua
+
+module('DBI', package.seeall)
+
+local name_to_module = {
+    MySQL = 'dbdmysql',
+    PostgreSQL = 'dbdpostgresql',
+    SQLite3 = 'dbdsqlite3',
+}
+
+local string = require('string')
+
+local function available_drivers()
+    local available = {}
+
+    for driver, modulefile in pairs(name_to_module) do
+	local m, err = pcall(require, modulefile)
+
+	if m then
+	    table.insert(available, driver)
+	end
+    end
+
+    if table.maxn(available) < 1 then
+	return '(None)'
+    end
+
+    return table.concat(available, ',')
+end
+
+function Connect(driver, name, username, password, host, port)
+    local modulefile = name_to_module[driver]
+
+    if not modulefile then
+	error(string.format("Driver '%s' not found. Available drivers are: %s", driver, available_drivers()))
+    end
+
+    local m, err = pcall(require, modulefile)
+
+    if not m then
+	error(string.format('Cannot load driver %s. Available drivers are: %s', driver, available_drivers()))
+    end
+
+    local class_str = string.format('DBD.%s.Connection', driver)
+
+    local connection_class = package.loaded[class_str]
+
+    return connection_class.New(name, username, password, host, port)
+end
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Makefile	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,54 @@
+CC=gcc
+CFLAGS=-g -pedantic -O2 -Wall -shared -fpic -I /usr/include/lua5.1 -I /usr/include/mysql -I /usr/include/postgresql/ -I . 
+AR=ar rcu
+RANLIB=ranlib
+RM=rm -f
+
+COMMON_LDFLAGS=
+MYSQL_LDFLAGS=$(COMMON_LDFLAGS) -lmysqlclient
+PSQL_LDFLAGS=$(COMMON_LDFLAGS) -lpq 
+SQLITE3_LDFLAGS=$(COMMON_LDFLAGS) -lsqlite3 
+
+DBDMYSQL=dbdmysql.so
+DBDPSQL=dbdpostgresql.so
+DBDSQLITE3=dbdsqlite3.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
+
+all:  dbdmysql	dbdpsql	dbdsqlite3
+
+dbdmysql: $(MYSQL_OBJS)
+	$(CC) $(CFLAGS) $(MYSQL_OBJS) -o $(DBDMYSQL) $(MYSQL_LDFLAGS)
+
+dbdpsql: $(PSQL_OBJS)
+	$(CC) $(CFLAGS) $(PSQL_OBJS) -o $(DBDPSQL) $(PSQL_LDFLAGS)
+
+dbdsqlite3: $(SQLITE3_OBJS)
+	$(CC) $(CFLAGS) $(SQLITE3_OBJS) -o $(DBDSQLITE3) $(SQLITE3_LDFLAGS)
+
+clean:
+	$(RM) $(MYSQL_OBJS) $(PSQL_OBJS) $(SQLITE3_OBJS) $(DBDMYSQL) $(DBDPSQL) $(DBDSQLITE3)
+
+build/dbd_mysql_connection.o: dbd/mysql/connection.c dbd/mysql/dbd_mysql.h dbd/common.h 
+	$(CC) -c -o $@ $< $(CFLAGS)
+build/dbd_mysql_main.o: dbd/mysql/main.c dbd/mysql/dbd_mysql.h dbd/common.h
+	$(CC) -c -o $@ $< $(CFLAGS)
+build/dbd_mysql_statement.o: dbd/mysql/statement.c dbd/mysql/dbd_mysql.h dbd/common.h
+	$(CC) -c -o $@ $< $(CFLAGS)
+
+build/dbd_postgresql_connection.o: dbd/postgresql/connection.c dbd/postgresql/dbd_postgresql.h dbd/common.h
+	$(CC) -c -o $@ $< $(CFLAGS)
+build/dbd_postgresql_main.o: dbd/postgresql/main.c dbd/postgresql/dbd_postgresql.h dbd/common.h
+	$(CC) -c -o $@ $< $(CFLAGS)
+build/dbd_postgresql_statement.o: dbd/postgresql/statement.c dbd/postgresql/dbd_postgresql.h dbd/common.h
+	$(CC) -c -o $@ $< $(CFLAGS)
+
+build/dbd_sqlite3_connection.o: dbd/sqlite3/connection.c dbd/sqlite3/dbd_sqlite3.h dbd/common.h 
+	$(CC) -c -o $@ $< $(CFLAGS)
+build/dbd_sqlite3_main.o: dbd/sqlite3/main.c dbd/sqlite3/dbd_sqlite3.h dbd/common.h
+	$(CC) -c -o $@ $< $(CFLAGS)
+build/dbd_sqlite3_statement.o: dbd/sqlite3/statement.c dbd/sqlite3/dbd_sqlite3.h dbd/common.h
+	$(CC) -c -o $@ $< $(CFLAGS)
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbd/common.h	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,78 @@
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+#if !defined(LUA_VERSION_NUM) || (LUA_VERSION_NUM < 501)
+#include <compat-5.1.h>
+#endif
+
+
+
+#define LUA_PUSH_ATTRIB_INT(n, v) \
+    lua_pushstring(L, n); \
+    lua_pushinteger(L, v); \
+    lua_rawset(L, -3); 
+
+#define LUA_PUSH_ATTRIB_FLOAT(n, v) \
+    lua_pushstring(L, n); \
+    lua_pushnumber(L, v); \
+    lua_rawset(L, -3); 
+
+#define LUA_PUSH_ATTRIB_STRING(n, v) \
+    lua_pushstring(L, n); \
+    lua_pushstring(L, v); \
+    lua_rawset(L, -3); 
+
+#define LUA_PUSH_ATTRIB_BOOL(n, v) \
+    lua_pushstring(L, n); \
+    lua_pushboolean(L, v); \
+    lua_rawset(L, -3); 
+
+#define LUA_PUSH_ATTRIB_NIL(n) \
+    lua_pushstring(L, n); \
+    lua_pushnil(L); \
+    lua_rawset(L, -3); 
+
+
+
+#define LUA_PUSH_ARRAY_INT(n, v) \
+    lua_pushinteger(L, v); \
+    lua_rawseti(L, -2, n); \
+    n++; 
+
+#define LUA_PUSH_ARRAY_FLOAT(n, v) \
+    lua_pushnumber(L, v); \
+    lua_rawseti(L, -2, n); \
+    n++; 
+
+#define LUA_PUSH_ARRAY_STRING(n, v) \
+    lua_pushstring(L, v); \
+    lua_rawseti(L, -2, n); \
+    n++;
+
+#define LUA_PUSH_ARRAY_BOOL(n, v) \
+    lua_pushboolean(L, v); \
+    lua_rawseti(L, -2, n); \
+    n++;
+
+#define LUA_PUSH_ARRAY_NIL(n) \
+    lua_pushnil(L); \
+    lua_rawseti(L, -2, n); \
+    n++;
+
+
+
+typedef enum lua_push_type {
+    LUA_PUSH_NIL = 0,
+    LUA_PUSH_INTEGER,
+    LUA_PUSH_NUMBER,
+    LUA_PUSH_STRING,
+    LUA_PUSH_BOOLEAN,
+
+    LUA_PUSH_MAX
+} lua_push_type_t;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbd/mysql/connection.c	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,122 @@
+#include "dbd_mysql.h"
+
+int dbd_mysql_statement_create(lua_State *L, connection_t *conn, const char *sql_query);
+
+static int connection_new(lua_State *L) {
+    int n = lua_gettop(L);
+
+    connection_t *conn = NULL;
+
+    const char *host = NULL;
+    const char *user = NULL;
+    const char *password = NULL;
+    const char *db = NULL;
+    int port = 0;
+
+    const char *unix_socket = NULL; /* TODO always NULL */
+    int client_flag = 0; /* TODO always 0, set flags from options table */
+
+    /* db, user, password, host, port */
+    switch (n) {
+    case 5:
+	if (lua_isnil(L, 5) == 0) 
+	    port = luaL_checkint(L, 5);
+    case 4: 
+	if (lua_isnil(L, 4) == 0) 
+	    host = luaL_checkstring(L, 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:
+	if (lua_isnil(L, 1) == 0) 
+	    db = luaL_checkstring(L, 1);
+    }
+
+    conn = (connection_t *)lua_newuserdata(L, sizeof(connection_t));
+
+    conn->mysql = mysql_init(NULL);
+
+    if (mysql_real_connect(conn->mysql, host, user, password, db, port, unix_socket, client_flag)) {
+        luaL_getmetatable(L, DBD_MYSQL_CONNECTION);
+        lua_setmetatable(L, -2);
+    } else {
+	luaL_error(L, "Failed to connect to database: %s", mysql_error(conn->mysql));
+	lua_pushnil(L);
+    }
+
+    return 1;
+}
+
+static int connection_close(lua_State *L) {
+    connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_MYSQL_CONNECTION);
+    int disconnect = 0;   
+
+    if (conn->mysql) {
+	mysql_close(conn->mysql);
+	disconnect = 1;
+    }
+
+    lua_pushboolean(L, disconnect);
+    return 1;
+}
+
+static int connection_ping(lua_State *L) {
+    connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_MYSQL_CONNECTION);
+    int err = 1;   
+
+    if (conn->mysql) {
+	err = mysql_ping(conn->mysql);
+    }
+
+    lua_pushboolean(L, !err);
+    return 1;
+}
+
+static int connection_prepare(lua_State *L) {
+    connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_MYSQL_CONNECTION);
+
+    if (conn->mysql) {
+	return dbd_mysql_statement_create(L, conn, luaL_checkstring(L, 2)); 
+    }
+
+    lua_pushnil(L);    
+    return 1;
+}
+
+
+static int connection_gc(lua_State *L) {
+    /* always close the connection */
+    connection_close(L);
+
+    return 0;
+}
+
+static const luaL_Reg connection_methods[] = {
+    {"close", connection_close},
+    {"ping", connection_ping},
+    {"prepare", connection_prepare},
+    {NULL, NULL}
+};
+
+static const luaL_Reg connection_class_methods[] = {
+    {"New", connection_new},
+    {NULL, NULL}
+};
+
+int dbd_mysql_connection(lua_State *L) {
+    luaL_newmetatable(L, DBD_MYSQL_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_MYSQL_CONNECTION, connection_class_methods);
+
+    return 1;    
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbd/mysql/dbd_mysql.h	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,16 @@
+#include <mysql.h>
+#include <dbd/common.h>
+
+#define DBD_MYSQL_CONNECTION	"DBD.MySQL.Connection"
+#define DBD_MYSQL_STATEMENT	"DBD.MySQL.Statement"
+
+typedef struct _connection {
+    MYSQL *mysql;
+} connection_t;
+
+typedef struct _statement {
+    MYSQL *mysql;
+    MYSQL_STMT *stmt;
+    MYSQL_RES *metadata;
+} statement_t;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbd/mysql/main.c	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,12 @@
+#include "dbd_mysql.h"
+
+int dbd_mysql_connection(lua_State *L);
+int dbd_mysql_statement(lua_State *L);
+
+int luaopen_dbdmysql(lua_State *L) {
+    dbd_mysql_connection(L);
+    dbd_mysql_statement(L);
+
+    return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbd/mysql/statement.c	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,302 @@
+#include "dbd_mysql.h"
+
+static lua_push_type_t mysql_to_lua_push(unsigned int mysql_type) {
+    lua_push_type_t lua_type;
+
+    switch(mysql_type) {
+    case MYSQL_TYPE_NULL:
+	lua_type = LUA_PUSH_NIL;
+	break;
+
+    case MYSQL_TYPE_TINY:
+    case MYSQL_TYPE_SHORT:
+    case MYSQL_TYPE_LONG:	
+	lua_type =  LUA_PUSH_INTEGER;
+	break;
+
+    case MYSQL_TYPE_DOUBLE:
+    case MYSQL_TYPE_LONGLONG:
+	lua_type = LUA_PUSH_NUMBER;
+	break;
+
+    default:
+	lua_type = LUA_PUSH_STRING;
+    }
+
+    return lua_type;
+} 
+
+static int statement_close(lua_State *L) {
+    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT);
+
+    if (statement->metadata) {
+	mysql_free_result(statement->metadata);
+    }
+
+    if (statement->stmt) {
+	mysql_stmt_close(statement->stmt);	
+    }
+
+    return 1;    
+}
+
+static int statement_execute(lua_State *L) {
+    int n = lua_gettop(L);
+    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT); 
+    int num_bind_params = n - 1;
+    
+    MYSQL_BIND *bind = NULL;
+    MYSQL_RES *metadata = NULL;
+
+    char *error_message = NULL;
+
+    int p;
+
+    bind = malloc(sizeof(MYSQL_BIND) * num_bind_params);
+    memset(bind, 0, sizeof(MYSQL_BIND) * num_bind_params);
+
+    for (p = 2; p <= n; p++) {
+	int type = lua_type(L, p);
+	int i = p - 2;
+
+	const char *str = NULL;
+	size_t str_len;
+
+	double num;
+
+	switch(type) {
+	    case LUA_TNIL:
+		bind[i].buffer_type = MYSQL_TYPE_NULL;
+		bind[i].is_null = (my_bool*)1;
+		break;
+
+	    case LUA_TNUMBER:
+		num = luaL_checknumber(L, p);
+
+		bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
+		bind[i].is_null = (my_bool*)0;
+		bind[i].buffer = (char *)&num;
+		bind[i].length = 0;
+		break;
+
+	    case LUA_TSTRING:
+		str = luaL_checklstring(L, p, &str_len);
+
+		bind[i].buffer_type = MYSQL_TYPE_STRING;
+		bind[i].is_null = (my_bool*)0;
+		bind[i].buffer = (char *)str;
+		bind[i].length = &str_len;
+		break;
+
+	    default:
+		error_message = "Binding unknown or unsupported type"; 
+		goto cleanup;
+	}
+    }
+
+    if (mysql_stmt_bind_param(statement->stmt, bind)) {
+	error_message = "Error binding statement parameters: %s";
+	goto cleanup;
+    }
+
+    if (mysql_stmt_execute(statement->stmt)) {
+	error_message = "Error executing statement: %s";
+	goto cleanup;
+    }
+
+    metadata = mysql_stmt_result_metadata(statement->stmt);
+
+cleanup:
+    if (bind)
+	free(bind);
+
+    if (error_message) {
+	luaL_error(L, error_message, mysql_stmt_error(statement->stmt));
+	return 0;
+    }
+
+    statement->metadata = metadata;
+
+    return 1;
+}
+
+static int statement_fetch_impl(lua_State *L, int named_columns) {
+    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_MYSQL_STATEMENT);
+    int column_count;
+    MYSQL_BIND *bind = NULL;
+    const char *error_message = NULL;
+
+    if (!statement->stmt) {
+	luaL_error(L, "fetch called before execute");
+	lua_pushnil(L);
+	return 1;
+    }
+
+    if (!statement->metadata) {
+	lua_pushnil(L);
+	return 1;
+    }
+
+    column_count = mysql_num_fields(statement->metadata);
+
+    if (column_count > 0) {
+	int i;
+	MYSQL_FIELD *fields;
+
+        bind = malloc(sizeof(MYSQL_BIND) * column_count);
+        memset(bind, 0, sizeof(MYSQL_BIND) * column_count);
+
+	fields = mysql_fetch_fields(statement->metadata);
+
+	for (i = 0; i < column_count; i++) {
+	    unsigned int length = fields[i].length;
+
+	    char *buffer = (char *)malloc(length);
+	    memset(buffer, 0, length);
+
+	    bind[i].buffer_type = fields[i].type; 
+	    bind[i].buffer = buffer;
+	    bind[i].buffer_length = length;
+	}
+
+	if (mysql_stmt_bind_result(statement->stmt, bind)) {
+	    error_message = "Error binding results: %s";
+	    goto cleanup;
+	}
+
+	if (!mysql_stmt_fetch(statement->stmt)) {
+	    int d = 1;
+
+	    lua_newtable(L);
+	    for (i = 0; i < column_count; i++) {
+		lua_push_type_t lua_push = mysql_to_lua_push(fields[i].type);
+		const char *name = fields[i].name;
+
+		if (lua_push == LUA_PUSH_NIL) {
+		    if (named_columns) {
+			LUA_PUSH_ATTRIB_NIL(name);
+		    } else {
+			LUA_PUSH_ARRAY_NIL(d);
+		    }
+		} else if (lua_push == LUA_PUSH_INTEGER) {
+		    if (named_columns) {
+			LUA_PUSH_ATTRIB_INT(name, *(int *)(bind[i].buffer)); 
+		    } else {
+			LUA_PUSH_ARRAY_INT(d, *(int *)(bind[i].buffer)); 
+		    }
+		} else if (lua_push == LUA_PUSH_NUMBER) {
+		    if (named_columns) {
+			LUA_PUSH_ATTRIB_FLOAT(name, *(double *)(bind[i].buffer));
+		    } else {
+			LUA_PUSH_ARRAY_FLOAT(d, *(double *)(bind[i].buffer));
+		    }
+		} else if (lua_push == LUA_PUSH_STRING) {
+		    if (named_columns) {
+			LUA_PUSH_ATTRIB_STRING(name, bind[i].buffer);
+		    } else {
+			LUA_PUSH_ARRAY_STRING(d, bind[i].buffer);
+		    }
+		} else if (lua_push == LUA_PUSH_BOOLEAN) {
+		    if (named_columns) {
+			LUA_PUSH_ATTRIB_BOOL(name, *(int *)(bind[i].buffer));
+		    } else {
+			LUA_PUSH_ARRAY_BOOL(d, *(int *)(bind[i].buffer));
+		    }
+		} else {
+		    luaL_error(L, "Unknown push type in result set");
+		}
+	    }
+	} else {
+	    lua_pushnil(L);	    
+	}
+    }
+
+cleanup:
+    if (bind) {
+	int i;
+
+	for (i = 0; i < column_count; i++) {
+	    free(bind[i].buffer);
+	}
+
+	free(bind);
+    }
+
+    if (error_message) {
+        luaL_error(L, error_message, mysql_stmt_error(statement->stmt));
+        return 0;
+    }
+
+    return 1;    
+}
+
+
+static int statement_fetch(lua_State *L) {
+    return statement_fetch_impl(L, 0);
+}
+
+static int statement_fetchtable(lua_State *L) {
+    return statement_fetch_impl(L, 1);
+}
+
+static int statement_gc(lua_State *L) {
+    /* always free the handle */
+    statement_close(L);
+
+    return 0;
+}
+
+
+static const luaL_Reg statement_methods[] = {
+    {"close", statement_close},
+    {"execute", statement_execute},
+    {"fetch", statement_fetch},
+    {"fetchtable", statement_fetchtable},
+    {NULL, NULL}
+};
+
+static const luaL_Reg statement_class_methods[] = {
+    {NULL, NULL}
+};
+
+int dbd_mysql_statement_create(lua_State *L, connection_t *conn, const char *sql_query) { 
+    unsigned long sql_len = strlen(sql_query);
+
+    statement_t *statement = NULL;
+
+    MYSQL_STMT *stmt = mysql_stmt_init(conn->mysql);
+
+    if (!stmt) {
+	luaL_error(L, "Error allocating statement handle: %s", mysql_error(conn->mysql));
+	return 0;
+    }
+
+    if (mysql_stmt_prepare(stmt, sql_query, sql_len)) {
+	luaL_error(L, "Error preparing statement handle: %s", mysql_stmt_error(stmt));
+	return 0;
+    }
+
+    statement = (statement_t *)lua_newuserdata(L, sizeof(statement_t));
+    statement->mysql = conn->mysql;
+    statement->stmt = stmt;
+    statement->metadata = NULL;
+
+    luaL_getmetatable(L, DBD_MYSQL_STATEMENT);
+    lua_setmetatable(L, -2);
+
+    return 1;
+} 
+
+int dbd_mysql_statement(lua_State *L) {
+    luaL_newmetatable(L, DBD_MYSQL_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_MYSQL_STATEMENT, statement_class_methods);
+
+    return 1;    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbd/postgresql/connection.c	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,135 @@
+#include "dbd_postgresql.h"
+
+int dbd_postgresql_statement_create(lua_State *L, connection_t *conn, const char *sql_query);
+
+static int connection_new(lua_State *L) {
+    int n = lua_gettop(L);
+    connection_t *conn = NULL;
+
+    const char *host = NULL;
+    const char *user = NULL;
+    const char *password = NULL;
+    const char *db = NULL;
+    const char *port = NULL;
+
+    const char *options = NULL; /* TODO always NULL */
+    const char *tty = NULL; /* TODO always NULL */
+
+    char portbuf[18];
+
+    /* db, user, password, host, port */
+    switch (n) {
+    case 5:
+	if (lua_isnil(L, 5) == 0) 
+	{
+	    int pport = luaL_checkint(L, 5);
+
+	    if (pport >= 1 && pport <= 65535) {
+		snprintf(portbuf, sizeof(portbuf), "%d", pport);
+		port = portbuf;
+	    } else {
+		luaL_error(L, "Invalid port %d", pport);
+	    }
+	}
+    case 4: 
+	if (lua_isnil(L, 4) == 0) 
+	    host = luaL_checkstring(L, 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:
+	if (lua_isnil(L, 1) == 0) 
+	    db = luaL_checkstring(L, 1);
+    }
+
+    conn = (connection_t *)lua_newuserdata(L, sizeof(connection_t));
+
+    conn->postgresql = PQsetdbLogin(host, port, options, tty, db, user, password);
+    conn->statement_id = 0;
+
+    if (PQstatus(conn->postgresql) == CONNECTION_OK) {
+        luaL_getmetatable(L, DBD_POSTGRESQL_CONNECTION);
+        lua_setmetatable(L, -2);
+    } else {
+	luaL_error(L, "Failed to connect to database: %s", PQerrorMessage(conn->postgresql));
+	lua_pushnil(L);
+    }
+
+    return 1;
+}
+
+static int connection_close(lua_State *L) {
+    connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_CONNECTION);
+    int disconnect = 0;   
+
+    if (conn->postgresql) {
+	PQfinish(conn->postgresql);
+	disconnect = 1;
+    }
+
+    lua_pushboolean(L, disconnect);
+    return 1;
+}
+
+static int connection_ping(lua_State *L) {
+    connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_CONNECTION);
+    int ok = 0;   
+
+    if (conn->postgresql) {
+	ConnStatusType status = PQstatus(conn->postgresql);
+
+	if (status == CONNECTION_OK)
+	    ok = 1;
+    }
+
+    lua_pushboolean(L, ok);
+    return 1;
+}
+
+static int connection_prepare(lua_State *L) {
+    connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_CONNECTION);
+
+    if (conn->postgresql) {
+	return dbd_postgresql_statement_create(L, conn, luaL_checkstring(L, 2));
+    }
+
+    lua_pushnil(L);    
+    return 1;
+}
+
+
+static int connection_gc(lua_State *L) {
+    /* always close the connection */
+    connection_close(L);
+
+    return 0;
+}
+
+static const luaL_Reg connection_methods[] = {
+    {"close", connection_close},
+    {"ping", connection_ping},
+    {"prepare", connection_prepare},
+    {NULL, NULL}
+};
+
+static const luaL_Reg connection_class_methods[] = {
+    {"New", connection_new},
+    {NULL, NULL}
+};
+
+int dbd_postgresql_connection(lua_State *L) {
+    luaL_newmetatable(L, DBD_POSTGRESQL_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_POSTGRESQL_CONNECTION, connection_class_methods);
+
+    return 1;    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbd/postgresql/dbd_postgresql.h	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,22 @@
+#include <libpq-fe.h>
+#include <postgres.h>
+#include <catalog/pg_type.h>
+#include <dbd/common.h>
+
+#define IDLEN 18
+
+#define DBD_POSTGRESQL_CONNECTION   "DBD.PostgreSQL.Connection"
+#define DBD_POSTGRESQL_STATEMENT    "DBD.PostgreSQL.Statement"
+
+typedef struct _connection {
+    PGconn *postgresql;
+    unsigned int statement_id;
+} connection_t;
+
+typedef struct _statement {
+    PGconn *postgresql;
+    PGresult *result;
+    char name[IDLEN];
+    int tuple;
+} statement_t;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbd/postgresql/main.c	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,12 @@
+#include "dbd_postgresql.h"
+
+int dbd_postgresql_connection(lua_State *L);
+int dbd_postgresql_statement(lua_State *L);
+
+int luaopen_dbdpostgresql(lua_State *L) {
+    dbd_postgresql_connection(L);
+    dbd_postgresql_statement(L); 
+
+    return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbd/postgresql/statement.c	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,319 @@
+#include "dbd_postgresql.h"
+
+#define MAX_PLACEHOLDERS	9999
+#define MAX_PLACEHOLDER_SIZE	(1+4) /* $\d{4} */
+
+static lua_push_type_t postgresql_to_lua_push(unsigned int postgresql_type) {
+    lua_push_type_t lua_type;
+
+    switch(postgresql_type) {
+    case INT2OID:
+    case INT4OID:
+        lua_type =  LUA_PUSH_INTEGER;
+        break;
+
+    case FLOAT4OID:
+    case FLOAT8OID:
+        lua_type = LUA_PUSH_NUMBER;
+        break;
+
+    case BOOLOID:
+	lua_type = LUA_PUSH_BOOLEAN;
+	break;
+
+    default:
+        lua_type = LUA_PUSH_STRING;
+    }
+
+    return lua_type;
+}
+
+static char *replace_placeholders(lua_State *L, const char *sql) {
+    size_t len = strlen(sql);
+    int num_placeholders = 0;
+    int extra_space = 0;
+    int i;
+    char *newsql;
+    int newpos = 1;
+    int ph_num = 1;
+    int in_quote = 0;
+
+    for (i = 1; i < len; i++) {
+	if (sql[i] == '?') {
+	    num_placeholders++;
+	}
+    }
+    
+    extra_space = num_placeholders * (MAX_PLACEHOLDER_SIZE-1); 
+
+    newsql = malloc(sizeof(char) * (len+extra_space+1));
+    memset(newsql, 0, sizeof(char) * (len+extra_space+1));
+    
+    /* copy first char */
+    newsql[0] = sql[0];
+    for (i = 1; i < len; i++) {
+	if (sql[i] == '\'' && sql[i-1] != '\\') {
+	    in_quote = !in_quote;
+	}
+
+	if (sql[i] == '?' && !in_quote) {
+	    size_t n;
+
+	    if (ph_num > MAX_PLACEHOLDERS) {
+		luaL_error(L, "Sorry, you are using more than %d placeholders. Use ${num} format instead", MAX_PLACEHOLDERS);
+	    }
+
+	    n = snprintf(&newsql[newpos], MAX_PLACEHOLDER_SIZE, "$%u", ph_num++);
+
+	    newpos += n;
+	} else {
+	    newsql[newpos] = sql[i];
+	    newpos++;
+	}
+    }
+
+    /* terminate string on the last position */
+    newsql[newpos] = '\0';
+
+    /* fprintf(stderr, "[%s]\n", newsql); */
+    return newsql;
+}
+
+static int statement_close(lua_State *L) {
+    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT);
+
+    if (statement->result) {
+	PQclear(statement->result);
+	statement->result = NULL;
+    }
+
+    return 0;    
+}
+
+static int statement_execute(lua_State *L) {
+    int n = lua_gettop(L);
+    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT);
+    int num_bind_params = n - 1;   
+    ExecStatusType status;
+    int p;
+
+    char **params;
+    PGresult *result = NULL;
+
+    statement->tuple = 0;
+
+    params = malloc(num_bind_params * sizeof(params));
+
+    for (p = 2; p <= n; p++) {
+	int i = p - 2;	
+
+	if (lua_isnil(L, p)) {
+	    params[i] = NULL;
+	} else {
+	    const char *param = lua_tostring(L, p);
+	    size_t len = strlen(param) + 1;
+
+	    params[i] = malloc(len * sizeof(char));
+	    memset(params[i], 0, len);
+
+	    strncpy(params[i], param, len);
+	    params[i][len] = '\0';
+	}
+    }
+
+    result = PQexecPrepared(
+	statement->postgresql,
+        statement->name,
+        num_bind_params,
+        (const char **)params,
+	NULL,
+        NULL,
+        0
+    );
+
+    for (p = 0; p < num_bind_params; p++) {
+	if (params[p]) {
+	    free(params[p]);
+	}
+    }
+    free(params);
+
+    if (!result) {
+	luaL_error(L, "Unable to allocate result handle");
+	lua_pushboolean(L, 0);
+	return 1;
+    }
+    
+    status = PQresultStatus(result);
+    if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
+	luaL_error(L, "Unable to execute statment: %s", PQresultErrorMessage(result));
+	lua_pushboolean(L, 0);
+	return 1;
+    }
+    
+    statement->result = result;
+
+    lua_pushboolean(L, 1);
+    return 1;
+}
+
+static int statement_fetch_impl(lua_State *L, int named_columns) {
+    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_POSTGRESQL_STATEMENT);
+    int tuple = statement->tuple++;
+    int i;
+    int num_columns;
+
+    if (PQresultStatus(statement->result) != PGRES_TUPLES_OK) {
+	lua_pushnil(L);
+	return 1;
+    }
+
+    if (tuple >= PQntuples(statement->result)) {
+	lua_pushnil(L);  /* no more results */
+	return 1;
+    }
+
+    num_columns = PQnfields(statement->result);
+    lua_newtable(L);
+    for (i = 0; i < num_columns; i++) {
+	int d = 1;
+	const char *name = PQfname(statement->result, i);
+
+	if (PQgetisnull(statement->result, tuple, i)) {
+	    if (named_columns) {
+		LUA_PUSH_ATTRIB_NIL(name);
+            } else {
+                LUA_PUSH_ARRAY_NIL(d);
+            }	    
+	} else {
+	    const char *value = PQgetvalue(statement->result, tuple, i);
+	    lua_push_type_t lua_push = postgresql_to_lua_push(PQftype(statement->result, i));
+
+            if (lua_push == LUA_PUSH_NIL) {
+                if (named_columns) {
+                    LUA_PUSH_ATTRIB_NIL(name);
+                } else {
+                    LUA_PUSH_ARRAY_NIL(d);
+                }
+            } else if (lua_push == LUA_PUSH_INTEGER) {
+                int val = atoi(value);
+
+                if (named_columns) {
+                    LUA_PUSH_ATTRIB_INT(name, val);
+                } else {
+                    LUA_PUSH_ARRAY_INT(d, val);
+                }
+            } else if (lua_push == LUA_PUSH_NUMBER) {
+                double val = strtod(value, NULL);
+
+                if (named_columns) {
+                    LUA_PUSH_ATTRIB_FLOAT(name, val);
+                } else {
+                    LUA_PUSH_ARRAY_FLOAT(d, val);
+                }
+            } else if (lua_push == LUA_PUSH_STRING) {
+                if (named_columns) {
+                    LUA_PUSH_ATTRIB_STRING(name, value);
+                } else {
+                    LUA_PUSH_ARRAY_STRING(d, value);
+                }
+            } else if (lua_push == LUA_PUSH_BOOLEAN) {
+                int val = value[0] == 't' ? 1 : 0;
+
+                if (named_columns) {
+                    LUA_PUSH_ATTRIB_BOOL(name, val);
+                } else {
+                    LUA_PUSH_ARRAY_BOOL(d, val);
+                }
+            } else {
+                luaL_error(L, "Unknown push type in result set");
+            }
+	}
+    }
+
+    return 1;    
+}
+
+
+static int statement_fetch(lua_State *L) {
+    return statement_fetch_impl(L, 0);
+}
+
+static int statement_fetchtable(lua_State *L) {
+    return statement_fetch_impl(L, 1);
+}
+
+static int statement_gc(lua_State *L) {
+    /* always free the handle */
+    statement_close(L);
+
+    return 0;
+}
+
+
+static const luaL_Reg statement_methods[] = {
+    {"close", statement_close},
+    {"execute", statement_execute},
+    {"fetch", statement_fetch},
+    {"fetchtable", statement_fetchtable},
+    {NULL, NULL}
+};
+
+static const luaL_Reg statement_class_methods[] = {
+    {NULL, NULL}
+};
+
+int dbd_postgresql_statement_create(lua_State *L, connection_t *conn, const char *sql_query) { 
+    statement_t *statement = NULL;
+    ExecStatusType status;
+    PGresult *result = NULL;
+    char *new_sql;
+    char name[IDLEN];
+
+    new_sql = replace_placeholders(L, sql_query);
+
+    snprintf(name, IDLEN, "%017u", ++conn->statement_id);
+
+    result = PQprepare(conn->postgresql, name, new_sql, 0, NULL);
+    free(new_sql);
+
+    if (!result) {
+	luaL_error(L, "Unable to allocate prepare result handle");
+    }
+    
+    status = PQresultStatus(result);
+    if (status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) {
+	const char *err_string = PQresultErrorMessage(result);
+	PQclear(result);
+	luaL_error(L, "Unable to prepare statment: %s", err_string);
+	return 0;
+    }
+
+    PQclear(result);
+
+    statement = (statement_t *)lua_newuserdata(L, sizeof(statement_t));
+    statement->postgresql = conn->postgresql;
+    statement->result = NULL;
+    statement->tuple = 0;
+    strncpy(statement->name, name, IDLEN-1);
+    statement->name[IDLEN-1] = '\0';
+
+    luaL_getmetatable(L, DBD_POSTGRESQL_STATEMENT);
+    lua_setmetatable(L, -2);
+
+    return 1;
+} 
+
+int dbd_postgresql_statement(lua_State *L) {
+    luaL_newmetatable(L, DBD_POSTGRESQL_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_POSTGRESQL_STATEMENT, statement_class_methods);
+
+    return 1;    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbd/sqlite3/connection.c	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,107 @@
+#include "dbd_sqlite3.h"
+
+int dbd_sqlite3_statement_create(lua_State *L, connection_t *conn, const char *sql_query);
+
+static int connection_new(lua_State *L) {
+    int n = lua_gettop(L);
+
+    const char *db = NULL;
+    connection_t *conn = NULL;
+
+    /* db */
+    switch (n) {
+    default:
+	if (lua_isnil(L, 1) == 0) 
+	    db = luaL_checkstring(L, 1);
+    }
+
+    conn = (connection_t *)lua_newuserdata(L, sizeof(connection_t));
+
+    if (sqlite3_open(db, &conn->sqlite) == SQLITE_OK) {
+        luaL_getmetatable(L, DBD_SQLITE_CONNECTION);
+        lua_setmetatable(L, -2);
+    } else {
+	luaL_error(L, "Failed to connect to database: %s", sqlite3_errmsg(conn->sqlite));
+	lua_pushnil(L);
+    }
+
+    return 1;
+}
+
+static int connection_close(lua_State *L) {
+    connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_SQLITE_CONNECTION);
+    int disconnect = 0;   
+
+    if (conn->sqlite) {
+#if 0
+	sqlite3_stmt *stmt;
+
+	while((stmt = sqlite3_next_stmt(conn->sqlite, NULL)) != 0){
+	    sqlite3_finalize(stmt);
+	}
+#endif
+	if (sqlite3_close(conn->sqlite) == SQLITE_OK) {
+	    disconnect = 1;
+	}
+    }
+
+    lua_pushboolean(L, disconnect);
+    return 1;
+}
+
+static int connection_ping(lua_State *L) {
+    connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_SQLITE_CONNECTION);
+    int ok = 0;   
+
+    if (conn->sqlite) {
+	ok = 1;
+    }
+
+    lua_pushboolean(L, ok);
+    return 1;
+}
+
+static int connection_prepare(lua_State *L) {
+    connection_t *conn = (connection_t *)luaL_checkudata(L, 1, DBD_SQLITE_CONNECTION);
+
+    if (conn->sqlite) {
+	return dbd_sqlite3_statement_create(L, conn, luaL_checkstring(L, 2));
+    }
+
+    lua_pushnil(L);    
+    return 1;
+}
+
+
+static int connection_gc(lua_State *L) {
+    /* always close the connection */
+    connection_close(L);
+
+    return 0;
+}
+
+static const luaL_Reg connection_methods[] = {
+    {"close", connection_close},
+    {"ping", connection_ping},
+    {"prepare", connection_prepare},
+    {NULL, NULL}
+};
+
+static const luaL_Reg connection_class_methods[] = {
+    {"New", connection_new},
+    {NULL, NULL}
+};
+
+int dbd_sqlite3_connection(lua_State *L) {
+    luaL_newmetatable(L, DBD_SQLITE_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_SQLITE_CONNECTION, connection_class_methods);
+
+    return 1;    
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbd/sqlite3/dbd_sqlite3.h	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,16 @@
+#include <sqlite3.h>
+#include <dbd/common.h>
+
+#define DBD_SQLITE_CONNECTION	"DBD.SQLite3.Connection"
+#define DBD_SQLITE_STATEMENT	"DBD.SQLite3.Statement"
+
+typedef struct _connection {
+    sqlite3 *sqlite;
+} connection_t;
+
+typedef struct _statement {
+    sqlite3_stmt *stmt;
+    sqlite3 *sqlite;
+    int more_data;
+} statement_t;
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbd/sqlite3/main.c	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,12 @@
+#include "dbd_sqlite3.h"
+
+int dbd_sqlite3_connection(lua_State *L);
+int dbd_sqlite3_statement(lua_State *L);
+
+int luaopen_dbdsqlite3(lua_State *L) {
+    dbd_sqlite3_connection(L);
+    dbd_sqlite3_statement(L); 
+
+    return 1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dbd/sqlite3/statement.c	Sun Nov 23 01:29:09 2008 +0000
@@ -0,0 +1,222 @@
+#include "dbd_sqlite3.h"
+
+static lua_push_type_t sqlite_to_lua_push(unsigned int sqlite_type) {
+    lua_push_type_t lua_type;
+
+    switch(sqlite_type) {
+    case SQLITE_NULL:
+        lua_type = LUA_PUSH_NIL;
+        break;
+
+    case SQLITE_INTEGER:
+        lua_type =  LUA_PUSH_INTEGER;
+        break;
+
+    case SQLITE_FLOAT:
+        lua_type = LUA_PUSH_NUMBER;
+        break;
+
+    default:
+        lua_type = LUA_PUSH_STRING;
+    }
+
+    return lua_type;
+}
+
+static int step(statement_t *statement) {
+    int res = sqlite3_step(statement->stmt);
+
+    if (res == SQLITE_DONE) {
+	statement->more_data = 0;
+	return 1;
+    } else if (res == SQLITE_ROW) {
+	statement->more_data = 1;
+	return 1;
+    }
+
+    return 0;
+}
+
+static int statement_close(lua_State *L) {
+    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_SQLITE_STATEMENT);
+    int ok = 0;
+
+    if (statement->stmt) {
+	if (sqlite3_finalize(statement->stmt) == SQLITE_OK) {
+	    ok = 1;
+	}
+    }
+
+    lua_pushboolean(L, ok);
+
+    return 1;
+}
+
+static int statement_execute(lua_State *L) {
+    int n = lua_gettop(L);
+    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_SQLITE_STATEMENT);
+    int p;
+
+    if (sqlite3_reset(statement->stmt) != SQLITE_OK) {
+	lua_pushboolean(L, 0);
+	return 1;
+    }
+
+    for (p = 2; p <= n; p++) {
+	int i = p - 1;
+
+	if (lua_isnil(L, p)) {
+	    if (sqlite3_bind_null(statement->stmt, i) != SQLITE_OK) {
+		luaL_error(L, "Failed to execute statement: %s", sqlite3_errmsg(statement->sqlite));
+	    }
+	} else if (lua_isnumber(L, p)) {
+	    if (sqlite3_bind_double(statement->stmt, i, luaL_checknumber(L, p)) != SQLITE_OK) {
+		luaL_error(L, "Failed to execute statement: %s", sqlite3_errmsg(statement->sqlite));
+	    }
+	} else if (lua_isstring(L, p)) {
+	    if (sqlite3_bind_text(statement->stmt, i, luaL_checkstring(L, p), -1, SQLITE_STATIC) != SQLITE_OK) {
+		luaL_error(L, "Failed to execute statement: %s", sqlite3_errmsg(statement->sqlite));
+	    }
+	}
+    }   
+
+    lua_pushboolean(L, step(statement));
+    return 1;
+}
+
+static int statement_fetch_impl(lua_State *L, int named_columns) {
+    statement_t *statement = (statement_t *)luaL_checkudata(L, 1, DBD_SQLITE_STATEMENT);
+    int num_columns;
+
+    if (!statement->more_data) {
+	lua_pushnil(L);
+	return 1;
+    }
+
+    num_columns = sqlite3_column_count(statement->stmt);
+
+    if (num_columns) {
+	int i;
+	int d = 1;
+
+	lua_newtable(L);
+
+	for (i = 0; i < num_columns; i++) {
+	    lua_push_type_t lua_push = sqlite_to_lua_push(sqlite3_column_type(statement->stmt, i));
+	    const char *name = sqlite3_column_name(statement->stmt, i);
+
+	    if (lua_push == LUA_PUSH_NIL) {
+                if (named_columns) {
+                    LUA_PUSH_ATTRIB_NIL(name);
+                } else {
+                    LUA_PUSH_ARRAY_NIL(d);
+                }
+            } else if (lua_push == LUA_PUSH_INTEGER) {
+		int val = sqlite3_column_int(statement->stmt, i);
+
+                if (named_columns) {
+                    LUA_PUSH_ATTRIB_INT(name, val);
+                } else {
+                    LUA_PUSH_ARRAY_INT(d, val);
+                }
+            } else if (lua_push == LUA_PUSH_NUMBER) {
+		double val = sqlite3_column_double(statement->stmt, i);
+
+                if (named_columns) {
+                    LUA_PUSH_ATTRIB_FLOAT(name, val);
+                } else {
+                    LUA_PUSH_ARRAY_FLOAT(d, val);
+                }
+            } else if (lua_push == LUA_PUSH_STRING) {
+		const char *val = (const char *)sqlite3_column_text(statement->stmt, i);
+
+                if (named_columns) {
+                    LUA_PUSH_ATTRIB_STRING(name, val);
+                } else {
+                    LUA_PUSH_ARRAY_STRING(d, val);
+                }
+            } else if (lua_push == LUA_PUSH_BOOLEAN) {
+		int val = sqlite3_column_int(statement->stmt, i);
+
+                if (named_columns) {
+                    LUA_PUSH_ATTRIB_BOOL(name, val);
+                } else {
+                    LUA_PUSH_ARRAY_BOOL(d, val);
+                }
+            } else {
+                luaL_error(L, "Unknown push type in result set");
+            }
+	}
+    }
+
+    if (step(statement) == 0) {
+	if (sqlite3_reset(statement->stmt) != SQLITE_OK) {
+	    luaL_error(L, "Failed to fetch statement: %s", sqlite3_errmsg(statement->sqlite));
+	}
+    }
+
+    return 1;    
+}
+
+
+static int statement_fetch(lua_State *L) {
+    return statement_fetch_impl(L, 0);
+}
+
+static int statement_fetchtable(lua_State *L) {
+    return statement_fetch_impl(L, 1);
+}
+
+static int statement_gc(lua_State *L) {
+    /* always free the handle */
+    statement_close(L);
+
+    return 0;
+}
+
+
+static const luaL_Reg statement_methods[] = {
+    {"close", statement_close},
+    {"execute", statement_execute},
+    {"fetch", statement_fetch},
+    {"fetchtable", statement_fetchtable},
+    {NULL, NULL}
+};
+
+static const luaL_Reg statement_class_methods[] = {
+    {NULL, NULL}
+};
+
+int dbd_sqlite3_statement_create(lua_State *L, connection_t *conn, const char *sql_query) { 
+    statement_t *statement = NULL;
+
+    statement = (statement_t *)lua_newuserdata(L, sizeof(statement_t));
+    statement->sqlite = conn->sqlite;
+    statement->stmt = NULL;
+    statement->more_data = 0;
+
+    if (sqlite3_prepare_v2(statement->sqlite, sql_query, strlen(sql_query), &statement->stmt, NULL) != SQLITE_OK) {
+	luaL_error(L, "Failed to prepare statement: %s", sqlite3_errmsg(statement->sqlite));	
+	lua_pushnil(L);
+	return 1;
+    } 
+
+    luaL_getmetatable(L, DBD_SQLITE_STATEMENT);
+    lua_setmetatable(L, -2);
+
+    return 1;
+} 
+
+int dbd_sqlite3_statement(lua_State *L) {
+    luaL_newmetatable(L, DBD_SQLITE_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_SQLITE_STATEMENT, statement_class_methods);
+
+    return 1;    
+}

mercurial