diff --git a/.gitattributes b/.gitattributes
index c0303b3e70ea3e9d2d7575b1a42cc6e7bf0a46b2..9d1468219b87066e9c9b33838bbb601d482ac3e6 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -54,6 +54,12 @@ testfont binary
 /src/modules/Mysql/mysql.c foreign_ident
 /src/modules/Mysql/precompiled_mysql.h foreign_ident
 /src/modules/Mysql/result.c foreign_ident
+/src/modules/Odbc/Makefile.in foreign_ident
+/src/modules/Odbc/acconfig.h foreign_ident
+/src/modules/Odbc/configure.in foreign_ident
+/src/modules/Odbc/odbc.c foreign_ident
+/src/modules/Odbc/odbc_result.c foreign_ident
+/src/modules/Odbc/precompiled_odbc.h foreign_ident
 /src/modules/Pipe/pipe.c foreign_ident
 /src/modules/Regexp/acconfig.h foreign_ident
 /src/modules/Ssleay/ssleay.c foreign_ident
diff --git a/src/modules/Odbc/.cvsignore b/src/modules/Odbc/.cvsignore
new file mode 100644
index 0000000000000000000000000000000000000000..3a5fcc9b7b5e80cc57e0cb8f16c49204a7e0abb6
--- /dev/null
+++ b/src/modules/Odbc/.cvsignore
@@ -0,0 +1,10 @@
+.pure
+Makefile
+config.h.in
+config.log
+config.status
+configure
+dependencies
+linker_options
+stamp-h
+stamp-h.in
diff --git a/src/modules/Odbc/.gitignore b/src/modules/Odbc/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..a5317fbd105b018042f2a89481eceaf676a128c1
--- /dev/null
+++ b/src/modules/Odbc/.gitignore
@@ -0,0 +1,10 @@
+/.pure
+/Makefile
+/config.h.in
+/config.log
+/config.status
+/configure
+/dependencies
+/linker_options
+/stamp-h
+/stamp-h.in
diff --git a/src/modules/Odbc/Makefile.in b/src/modules/Odbc/Makefile.in
new file mode 100644
index 0000000000000000000000000000000000000000..a2ba0da840f110595934c343c77870e848b671e9
--- /dev/null
+++ b/src/modules/Odbc/Makefile.in
@@ -0,0 +1,12 @@
+#
+# $Id: Makefile.in,v 1.1 1997/03/10 19:01:49 grubba Exp $
+#
+
+SRCDIR=@srcdir@
+VPATH=@srcdir@:@srcdir@/../..:../..
+MODULE_CPPFLAGS=@DEFS@ @CPPFLAGS@
+OBJS=odbc.o odbc_result.o
+MODULE_LDFLAGS=@LDFLAGS@ @ODBC_LIBS@
+
+@dynamic_module_makefile@
+@dependencies@
\ No newline at end of file
diff --git a/src/modules/Odbc/acconfig.h b/src/modules/Odbc/acconfig.h
new file mode 100644
index 0000000000000000000000000000000000000000..ada3907ce4a43823839619fe7c862ae5d8fdde64
--- /dev/null
+++ b/src/modules/Odbc/acconfig.h
@@ -0,0 +1,18 @@
+/*
+ * $Id: acconfig.h,v 1.1 1997/03/10 19:01:50 grubba Exp $
+ *
+ * Config-file for the Pike ODBC-module.
+ *
+ * Henrik Grubbstr�m
+ */
+
+#ifndef PIKE_ODBC_CONFIG_H
+#define PIKE_ODBC_CONFIG_H
+
+@TOP@
+@BOTTOM@
+
+/* Define if you have ODBC */
+#undef HAVE_ODBC
+
+#endif /* PIKE_ODBC_CONFIG_H */
diff --git a/src/modules/Odbc/configure.in b/src/modules/Odbc/configure.in
new file mode 100644
index 0000000000000000000000000000000000000000..596801eeeeed8ccfb739ab8b9489c82cf0aa1283
--- /dev/null
+++ b/src/modules/Odbc/configure.in
@@ -0,0 +1,127 @@
+#
+# $Id: configure.in,v 1.1 1997/03/10 19:01:51 grubba Exp $
+#
+# Configure script for the odbc-module
+#
+# Henrik Grubbstr�m
+#
+
+AC_INIT(odbc.c)
+AC_CONFIG_HEADER(config.h)
+
+sinclude(../module_configure.in)
+
+OLD_LIBS=$LIBS
+OLD_LDFLAGS=$LDFLAGS
+OLD_CPPFLAGS=$CPPFLAGS
+ODBC_LIBS=""
+
+AC_ARG_WITH(odbc,  [  --without-odbc       no support for the ODBC databases],[],[with_odbc=yes])
+
+if test x$with_odbc = xyes; then
+
+  AC_MSG_CHECKING(Checking for ODBC library-directory)
+
+  AC_CACHE_VAL(pike_cv_odbc_lib_dir, [
+    for pike_cv_odbc_lib_dir in ${INFORMIXDIR:+$INFORMIXDIR/cli/dlls} /opt/ISLIodbc/*/lib /usr/opt/ISLIodbc/*/lib /usr/local/lib /usr/local/odbc/lib /usr/local/lib/odbc /usr/odbc/lib /usr/lib/odbc /usr/lib /lib/odbc /lib no; do
+      echo $pike_cv_odbc_lib_dir
+      if test -d $pike_cv_odbc_lib_dir/.; then
+        if ls $pike_cv_odbc_lib_dir/*odbc* >/dev/null 2>&1 ; then
+	  break
+	else
+	  :
+	fi
+      else
+        :
+      fi
+    done
+  ])
+
+  AC_MSG_RESULT($pike_cv_odbc_lib_dir)
+
+  if test x$pike_cv_odbc_lib_dir = xno; then :; else
+    echo Adding $pike_cv_odbc_lib_dir to the library search path.
+    LDFLAGS="-L$pike_cv_odbc_lib_dir ${LDFLAGS}"
+  fi
+
+  AC_MSG_CHECKING(Checking for the ODBC include-directory)
+
+  AC_CACHE_VAL(pike_cv_odbc_include_dir, [
+    for pike_cv_odbc_include_dir in ${INFORMIXDIR:+$INFORMIXDIR/cli/include} /opt/ISLIodbc/*/include /usr/local/include /usr/local/odbc/include /usr/local/include/odbc /usr/odbc/include /usr/include/odbc /usr/include /include/odbc /include no; do
+      echo $pike_cv_odbc_include_dir
+      if test -d $pike_cv_odbc_include_dir/.; then
+        if ls $pike_cv_odbc_include_dir/qeodbc.h >/dev/null 2>&1; then
+          break
+	else
+	  :
+	fi
+      else
+	:
+      fi
+    done
+  ])
+
+  AC_MSG_RESULT($pike_cv_odbc_include_dir)
+
+  if test x$pike_cv_odbc_include_dir = xno; then :; else
+    echo Adding $pike_cv_odbc_include_dir to the include search path.
+    CPPFLAGS="-I$pike_cv_odbc_include_dir ${CPPFLAGS}"
+  fi
+
+  # Header file
+
+  AC_CHECK_HEADERS(qeodbc.h sql.h sqlext.h)
+
+  # System libs which might be needed
+
+  if echo $LIBS|grep -- -lsocket >&5 2>&5; then
+    :
+  else
+    AC_CHECK_LIB(socket, socket, [
+      LIBS="-lsocket $LIBS"
+      ODBC_LIBS="-lsocket ${ODBC_LIBS}"
+    ], [])
+  fi
+  if echo $LIBS|grep -- -lnsl >&5 2>&5; then
+    :
+  else
+    AC_CHECK_LIB(nsl, gethostbyname, [
+      LIBS="-lnsl $LIBS"
+      ODBC_LIBS="-lnsl ${ODBC_LIBS}"
+    ], [])
+  fi
+  if echo $LIBS|grep -- -lm >&5 2>&5; then
+    :
+  else
+    AC_CHECK_LIB(m, floor, [
+      LIBS="-lm $LIBS"
+      ODBC_LIBS="-lm ${ODBC_LIBS}"
+    ], [])
+  fi
+
+  pike_cv_odbc=no;
+
+  AC_CHECK_LIB(odbc, SQLConnect, [
+    LIBS="-lodbc $LIBS"
+    ODBC_LIBS="-lodbc ${ODBC_LIBS}"
+    pike_cv_odbc=yes;
+  ], [])
+
+  if test x$pike_cv_odbc = xno; then
+    # Restore variables, so we don't link with unnessesary libs
+
+    LIBS=$OLD_LIBS
+    CPPFLAGS=$OLD_CPPFLAGS
+    LDFLAGS=$OLD_LDFLAGS
+    ODBC_LIBS=""
+  else
+    AC_DEFINE(HAVE_ODBC)
+
+  fi
+else
+  :
+fi
+
+AC_SUBST(ODBC_LIBS)
+
+AC_OUTPUT(Makefile,echo FOO >stamp-h )
diff --git a/src/modules/Odbc/odbc.c b/src/modules/Odbc/odbc.c
new file mode 100644
index 0000000000000000000000000000000000000000..427b31b7e85739bf11221e73bd8cdcd03877f9d7
--- /dev/null
+++ b/src/modules/Odbc/odbc.c
@@ -0,0 +1,334 @@
+/*
+ * $Id: odbc.c,v 1.1 1997/03/10 19:01:52 grubba Exp $
+ *
+ * Pike interface to ODBC compliant databases.
+ *
+ * Henrik Grubbstr�m
+ */
+
+/*
+ * Includes
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "global.h"
+RCSID("$Id: odbc.c,v 1.1 1997/03/10 19:01:52 grubba Exp $");
+
+#include "interpret.h"
+#include "object.h"
+#include "threads.h"
+#include "svalue.h"
+#include "stralloc.h"
+#include "mapping.h"
+#include "array.h"
+#include "multiset.h"
+#include "program.h"
+
+#include "precompiled_odbc.h"
+
+/*
+ * Globals
+ */
+
+struct program *odbc_program = NULL;
+
+/*
+ * Functions
+ */
+
+/*
+ * Helper functions
+ */
+
+volatile void odbc_error(const char *fun, const char *msg,
+			 struct precompiled_odbc *odbc, HSTMT hstmt,
+			 RETCODE code, void (*clean)(void));
+
+static INLINE volatile void odbc_check_error(const char *fun, const char *msg,
+					     RETCODE code, void (*clean)(void))
+{
+  if ((code != SQL_SUCCESS) && (code != SQL_SUCCESS_WITH_INFO)) {
+    odbc_error(fun, msg, PIKE_ODBC, PIKE_ODBC->hstmt, code, clean);
+  }
+}
+
+volatile void odbc_error(const char *fun, const char *msg,
+			 struct precompiled_odbc *odbc, HSTMT hstmt,
+			 RETCODE code, void (*clean)(void))
+{
+  RETCODE _code;
+  char errcode[256];
+  char errmsg[SQL_MAX_MESSAGE_LENGTH];
+  SWORD errmsg_len = 0;
+  SDWORD native_error;
+
+  _code = SQLError(odbc->henv, odbc->hdbc, hstmt, errcode, &native_error,
+		   errmsg, SQL_MAX_MESSAGE_LENGTH-1, &errmsg_len);
+  errmsg[errmsg_len] = '\0';
+
+  if (odbc) {
+    if (odbc->last_error) {
+      free_string(odbc->last_error);
+    }
+    odbc->last_error = make_shared_binary_string(errmsg, errmsg_len);
+  }
+
+  if (clean) {
+    clean();
+  }
+  switch(_code) {
+  case SQL_SUCCESS:
+  case SQL_SUCCESS_WITH_INFO:
+    error("%s():%s: %d:%s:%s\n", fun, msg, code, errcode, errmsg);
+    break;
+  default:
+    error("%s():%s: SQLError failed (%d:%d)\n", fun, msg, code, _code);
+  }
+}
+
+/*
+ * Clean-up functions
+ */
+
+static void clean_last_error(void)
+{
+  if (PIKE_ODBC->last_error) {
+    free_string(PIKE_ODBC->last_error);
+    PIKE_ODBC->last_error = NULL;
+  }
+}
+
+static void clean_henv(void)
+{
+  HENV henv = PIKE_ODBC->henv;
+  PIKE_ODBC->henv = SQL_NULL_HENV;
+  odbc_check_error("odbc_error", "Freeing HENV",
+		   SQLFreeEnv(henv), clean_last_error);
+}
+
+static void clean_hdbc(void)
+{
+  HDBC hdbc = PIKE_ODBC->hdbc;
+  PIKE_ODBC->hdbc = SQL_NULL_HDBC;
+  odbc_check_error("odbc_error", "Disconnecting HDBC",
+		   SQLDisconnect(hdbc), clean_henv);
+  odbc_check_error("odbc_error", "Freeing HDBC",
+		   SQLFreeConnect(hdbc), clean_henv);
+}
+
+/*
+ * Glue functions
+ */
+
+static void init_odbc_struct(struct object *o)
+{
+  RETCODE code;
+
+  PIKE_ODBC->henv = SQL_NULL_HENV;
+  PIKE_ODBC->hdbc = SQL_NULL_HDBC;
+  PIKE_ODBC->hstmt = SQL_NULL_HSTMT;
+  PIKE_ODBC->last_error = NULL;
+  PIKE_ODBC->affected_rows = 0;
+
+  odbc_check_error("init_odbc_struct", "ODBC initialization failed",
+		   SQLAllocEnv(&(PIKE_ODBC->henv)), 0);
+
+  odbc_check_error("init_odbc_struct", "ODBC initialization failed",
+		   SQLAllocConnect(PIKE_ODBC->henv, &(PIKE_ODBC->hdbc)),
+		   clean_henv);
+}
+
+static void exit_odbc_struct(struct object *o)
+{
+  RETCODE code;
+  HENV henv = PIKE_ODBC->henv;
+  HDBC hdbc = PIKE_ODBC->hdbc;
+
+  PIKE_ODBC->hdbc = SQL_NULL_HDBC;
+  odbc_check_error("exit_odbc_struct", "ODBC disconnect failed",
+		   SQLDisconnect(hdbc), clean_henv);
+  odbc_check_error("exit_odbc_struct", "Freeing ODBC connection failed",
+		   SQLFreeConnect(hdbc), clean_henv);
+  PIKE_ODBC->henv = SQL_NULL_HENV;
+  odbc_check_error("exit_odbc_struct", "Freeing ODBC environment failed",
+		   SQLFreeEnv(henv), clean_last_error);
+}
+
+/*
+ * Pike functions
+ */
+
+static void f_error(INT32 args)
+{
+  pop_n_elems(args);
+
+  if (PIKE_ODBC->last_error) {
+    PIKE_ODBC->last_error->refs++;
+    push_string(PIKE_ODBC->last_error);
+  } else {
+    push_int(0);
+  }
+}
+
+static void f_create(INT32 args)
+{
+  struct pike_string *server = NULL;
+  struct pike_string *database = NULL;
+  struct pike_string *user = NULL;
+  struct pike_string *pwd = NULL;
+
+  get_args(sp-args, args, "%S%S%S%S", &server, &database, &user, &pwd);
+
+  /*
+   * NOTE:
+   *
+   * database argument is ignored
+   */
+
+  if (!pwd) {
+    pwd = make_shared_string("");
+  }
+  if (!user) {
+    user = make_shared_string("");
+  }
+  if (!server) {
+    server = make_shared_string("");
+  }
+  odbc_check_error("odbc->create", "Connect failed",
+		   SQLConnect(PIKE_ODBC->hdbc, server->str, server->len,
+			      user->str, user->len, pwd->str, pwd->len), NULL);
+  pop_n_elems(args);
+}
+
+static void f_affected_rows(INT32 args)
+{
+  pop_n_elems(args);
+  push_int(PIKE_ODBC->affected_rows);
+}
+
+static void f_insert_id(INT32 args)
+{
+  /**********************************************/
+}
+
+static void f_select_db(INT32 args)
+{
+  /**********************************************/
+}
+
+static void f_big_query(INT32 args)
+{
+  HSTMT hstmt = SQL_NULL_HSTMT;
+  struct pike_string *q = NULL;
+
+  get_all_args("odbc->big_query", args, "%S", &q);
+
+  odbc_check_error("odbc->big_query", "Statement allocation failed",
+		   SQLAllocStmt(PIKE_ODBC->hdbc, &hstmt), NULL);
+  odbc_check_error("odbc->big_query", "Query failed",
+		   SQLExecDirect(hstmt, q->str, q->len), NULL);
+  pop_n_elems(args);
+
+  odbc_check_error("odbc->big_query", "Couldn't get the number of fields",
+		   SQLNumResultCols(hstmt, &(PIKE_ODBC->num_fields)), NULL);
+
+  odbc_check_error("odbc->big_query", "Couldn't get the number of rows",
+		   SQLRowCount(hstmt, &(PIKE_ODBC->affected_rows)), NULL);
+
+  pop_n_elems(args);
+
+  if (PIKE_ODBC->num_fields) {
+    PIKE_ODBC->hstmt=hstmt;
+    push_object(fp->current_object);
+    fp->current_object->refs++;
+
+    push_object(clone(odbc_result_program, 1));
+  } else {
+    odbc_check_error("odbc->big_query", "Couldn't commit query",
+		     SQLTransact(PIKE_ODBC->henv, PIKE_ODBC->hdbc,
+				 SQL_COMMIT), NULL);
+    push_int(0);
+  }
+}
+
+static void f_create_db(INT32 args)
+{
+  /**************************************************/
+}
+
+static void f_drop_db(INT32 args)
+{
+  /**************************************************/
+}
+
+static void f_shutdown(INT32 args)
+{
+  /**************************************************/
+}
+
+static void f_reload(INT32 args)
+{
+  /**************************************************/
+}
+
+
+
+/*
+ * Module linkage
+ */
+
+void pike_module_init(void)
+{
+#ifdef HAVE_ODBC
+  start_new_program();
+  add_storage(sizeof(struct precompiled_odbc));
+
+  add_function("error", f_error, "function(void:int|string)", ID_PUBLIC);
+  add_function("create", f_create, "function(string|void, string|void, string|void, string|void:void)", ID_PUBLIC);
+  add_function("affected_rows", f_affected_rows, "function(void:int)", ID_PUBLIC);
+  add_function("insert_id", f_insert_id, "function(void:int)", ID_PUBLIC);
+  add_function("select_db", f_select_db, "function(string:void)", ID_PUBLIC);
+  add_function("big_query", f_big_query, "function(string:int|object)", ID_PUBLIC);
+  add_function("create_db", f_create_db, "function(string:void)", ID_PUBLIC);
+  add_function("drop_db", f_drop_db, "function(string:void)", ID_PUBLIC);
+  add_function("shutdown", f_shutdown, "function(void:void)", ID_PUBLIC);
+  add_function("reload", f_reload, "function(void:void)", ID_PUBLIC);
+#if 0
+  add_function("statistics", f_statistics, "function(void:string)", ID_PUBLIC);
+  add_function("server_info", f_server_info, "function(void:string)", ID_PUBLIC);
+  add_function("host_info", f_host_info, "function(void:string)", ID_PUBLIC);
+  add_function("protocol_info", f_protocol_info, "function(void:int)", ID_PUBLIC);
+  add_function("list_dbs", f_list_dbs, "function(void|string:object)", ID_PUBLIC);
+  add_function("list_tables", f_list_tables, "function(void|string:object)", ID_PUBLIC);
+  add_function("list_fields", f_list_fields, "function(string, void|string:array(int|mapping(string:mixed)))", ID_PUBLIC);
+  add_function("list_processes", f_list_processes, "function(void|string:object)", ID_PUBLIC);
+ 
+  add_function("binary_data", f_binary_data, "function(void:int)", ID_PUBLIC);
+#endif /* 0 */
+ 
+  set_init_callback(init_odbc_struct);
+  set_exit_callback(exit_odbc_struct);
+ 
+  odbc_program = end_program();
+  add_program_constant("odbc", odbc_program, 0);
+ 
+  init_odbc_res_programs();
+
+#endif /* HAVE_ODBC */
+}
+
+void pike_module_exit(void)
+{
+#ifdef HAVE_ODBC
+  exit_odbc_res();
+ 
+  if (odbc_program) {
+    free_program(odbc_program);
+    odbc_program = NULL;
+  }
+#endif /* HAVE_ODBC */
+}
+ 
diff --git a/src/modules/Odbc/odbc_result.c b/src/modules/Odbc/odbc_result.c
new file mode 100644
index 0000000000000000000000000000000000000000..545e30b9aaee59d64b5a1f3e4c71a7e50e3c486b
--- /dev/null
+++ b/src/modules/Odbc/odbc_result.c
@@ -0,0 +1,428 @@
+/*
+ * $Id: odbc_result.c,v 1.1 1997/03/10 19:01:52 grubba Exp $
+ *
+ * Pike  interface to ODBC compliant databases
+ *
+ * Henrik Grubbstr�m
+ */
+
+/*
+ * Includes
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#ifdef HAVE_ODBC
+
+#include "global.h"
+RCSID("$Id: odbc_result.c,v 1.1 1997/03/10 19:01:52 grubba Exp $");
+
+#include "interpret.h"
+#include "object.h"
+#include "threads.h"
+#include "svalue.h"
+#include "stralloc.h"
+#include "mapping.h"
+#include "array.h"
+#include "multiset.h"
+#include "program.h"
+
+#include "precompiled_odbc.h"
+
+/*
+ * Globals
+ */
+
+struct program *odbc_result_program = NULL;
+
+/*
+ * Functions
+ */
+
+/*
+ * Help functions
+ */
+
+static void clean_sql_res(void)
+{
+  if (PIKE_ODBC_RES->field_info) {
+    free(PIKE_ODBC_RES->field_info);
+    PIKE_ODBC_RES->field_info = NULL;
+  }
+  if (PIKE_ODBC_RES->fields) {
+    free_array(PIKE_ODBC_RES->fields);
+    PIKE_ODBC_RES->fields = NULL;
+  }
+  if (PIKE_ODBC_RES->obj) {
+    free_object(PIKE_ODBC_RES->obj);
+    PIKE_ODBC_RES->obj = NULL;
+    PIKE_ODBC_RES->odbc = NULL;
+  }
+  PIKE_ODBC_RES->hstmt = SQL_NULL_HSTMT;
+}
+
+static INLINE volatile void odbc_check_error(const char *fun, const char *msg,
+					     RETCODE code, void (*clean)(void))
+{
+  if ((code != SQL_SUCCESS) && (code != SQL_SUCCESS_WITH_INFO)) {
+    odbc_error(fun, msg, PIKE_ODBC_RES->odbc, PIKE_ODBC_RES->hstmt,
+	       code, clean);
+  }
+}
+
+/*
+ * State maintenance
+ */
+ 
+static void init_res_struct(struct object *o)
+{
+  memset(PIKE_ODBC_RES, 0, sizeof(struct precompiled_odbc_result));
+  PIKE_ODBC_RES->hstmt = SQL_NULL_HSTMT;
+}
+ 
+static void exit_res_struct(struct object *o)
+{
+  if (PIKE_ODBC_RES->hstmt != SQL_NULL_HSTMT) {
+    HSTMT hstmt = PIKE_ODBC_RES->hstmt;
+    PIKE_ODBC_RES->hstmt = SQL_NULL_HSTMT;
+    odbc_check_error("exit_res_struct", "Freeing of HSTMT failed",
+		     SQLFreeStmt(hstmt, SQL_DROP), clean_sql_res);
+  }
+  clean_sql_res();
+}
+
+/*
+ * More help functions
+ */
+
+static void odbc_fix_fields(void)
+{
+  int i;
+  struct odbc_field_info {
+    int type;	/* Pike-type */
+    int size;
+  } *odbc_fields = alloca(sizeof(struct odbc_field_info)*PIKE_ODBC_RES->num_fields);
+  size_t buf_size = 1024;
+  char *buf = alloca(buf_size);
+  int membuf_size = 0;
+  char *membuf = NULL;
+
+  if ((!buf)||(!odbc_fields)) {
+    error("odbc_fix_fields(): Out of memory\n");
+  }
+
+  /*
+   * First build the fields-array;
+   */
+  for (i=0; i < PIKE_ODBC_RES->num_fields; i++) {
+    int nbits;
+    SWORD name_len;
+    SWORD sql_type;
+    UDWORD precision;
+    SWORD scale;
+    SWORD nullable;
+
+    while (1) {
+      odbc_check_error("odbc_fix_fields", "Failed to fetch field info",
+		       SQLDescribeCol(PIKE_ODBC_RES->hstmt, i+1,
+				      buf, buf_size, &name_len,
+				      &sql_type, &precision, &scale, &nullable),
+		       0);
+      if (name_len < (int)buf_size) {
+	break;
+      }
+      do {
+	buf_size *= 2;
+      } while (name_len >= (int)buf_size);
+      if (!(buf = alloca(buf_size))) {
+	error("odbc_fix_fields(): Out of memory\n");
+      }
+    }
+    odbc_fields[i].type = T_STRING;
+    odbc_fields[i].size = precision+1;
+    /* Create the mapping */
+    push_text("name"); push_string(make_shared_binary_string(buf, name_len));
+    push_text("type");
+    switch(sql_type) {
+    case SQL_CHAR:
+      push_text("char");
+      break;
+    case SQL_DATE:
+      push_text("date");
+      odbc_fields[i].size = 7;
+      break;
+    case SQL_DECIMAL:
+      push_text("decimal");
+      odbc_fields[i].size += 2;
+      break;
+    case SQL_DOUBLE:
+      push_text("double");
+      odbc_fields[i].size = 8;
+      odbc_fields[i].type = T_FLOAT;
+      break;
+    case SQL_INTEGER:
+      push_text("integer");
+      odbc_fields[i].size = 4;
+      odbc_fields[i].type = T_INT;
+      break;
+    case SQL_LONGVARBINARY:
+      push_text("long blob");
+      break;
+    case SQL_LONGVARCHAR:
+      push_text("var string");
+      break;
+    case SQL_REAL:
+      push_text("float");
+      odbc_fields[i].size = 4;
+      odbc_fields[i].type = T_FLOAT;
+      break;
+    case SQL_SMALLINT:
+      push_text("short");
+      odbc_fields[i].size = 4;
+      odbc_fields[i].type = T_INT;
+      break;
+    case SQL_TIMESTAMP:
+      push_text("time");
+      odbc_fields[i].size = 22 + scale;
+      break;
+    case SQL_VARCHAR:
+      push_text("var string");
+      break;
+    default:
+      push_text("unknown");
+      break;
+    }
+    push_text("length"); push_int(precision);
+    push_text("decimals"); push_int(scale);
+    push_text("flags");
+    nbits = 0;
+    if (nullable == SQL_NULLABLE) {
+      nbits++;
+      push_text("nullable");
+    }
+    if ((sql_type == SQL_LONGVARCHAR) ||
+	(sql_type == SQL_LONGVARBINARY)) {
+      nbits++;
+      push_text("blob");
+    }
+    f_aggregate_multiset(nbits);
+
+    f_aggregate_mapping(5*2);
+
+    /* Align to long-word size */
+    odbc_fields[i].size += 3;
+    odbc_fields[i].size &= ~3;
+
+    membuf_size += odbc_fields[i].size;
+  }
+  f_aggregate_array(PIKE_ODBC_RES->num_fields);
+
+  sp[-1].u.array->refs++;
+  PIKE_ODBC_RES->fields = sp[-1].u.array;
+  pop_stack();
+
+  PIKE_ODBC_RES->field_info = (struct field_info *)
+    xalloc(sizeof(struct field_info) * PIKE_ODBC_RES->num_fields +
+	   membuf_size);
+  membuf = ((char *) PIKE_ODBC_RES->field_info) +
+    sizeof(struct field_info) * PIKE_ODBC_RES->num_fields;
+
+  /*
+   * Now it's time to bind the columns
+   */
+  for (i=0; i < PIKE_ODBC_RES->num_fields; i++) {
+    PIKE_ODBC_RES->field_info[i].type = odbc_fields[i].type;
+    PIKE_ODBC_RES->field_info[i].buf = membuf;
+    PIKE_ODBC_RES->field_info[i].size = odbc_fields[i].size;
+    
+    switch(odbc_fields[i].type) {
+    case T_STRING:
+      odbc_check_error("odbc_fix_fields", "Couldn't bind string field",
+		       SQLBindCol(PIKE_ODBC_RES->hstmt, i+1,
+				  SQL_C_CHAR, membuf, odbc_fields[i].size,
+				  &PIKE_ODBC_RES->field_info[i].len), NULL);
+      break;
+    case T_INT:
+      odbc_check_error("odbc_fix_fields", "Couldn't bind integer field",
+		       SQLBindCol(PIKE_ODBC_RES->hstmt, i+1,
+				  SQL_C_SLONG, membuf, odbc_fields[i].size,
+				  &PIKE_ODBC_RES->field_info[i].len), NULL);
+      break;
+    case T_FLOAT:
+      odbc_check_error("odbc_fix_fields", "Couldn't bind float field",
+		       SQLBindCol(PIKE_ODBC_RES->hstmt, i+1,
+				  SQL_C_DOUBLE, membuf, odbc_fields[i].size,
+				  &PIKE_ODBC_RES->field_info[i].len), NULL);
+      break;
+    default:
+      error("odbc_fix_fields(): Internal error: Unhandled type:%d\n",
+	    odbc_fields[i].type);
+      break;
+    }
+    membuf += odbc_fields[i].size;
+  }
+}
+
+/*
+ * Methods
+ */
+
+/* void create(object(odbc)) */
+static void f_create(INT32 args)
+{
+  if (!args) {
+    error("Too few arguments to odbc_result()\n");
+  }
+  if ((sp[-args].type != T_OBJECT) ||
+      (sp[-args].u.object->prog != odbc_program)) {
+    error("Bad argument 1 to odbc_result()\n");
+  }
+ 
+  PIKE_ODBC_RES->obj = sp[-args].u.object;
+  PIKE_ODBC_RES->obj->refs++;
+  PIKE_ODBC_RES->odbc = ((struct precompiled_odbc *)sp[-args].u.object->storage);
+  PIKE_ODBC_RES->hstmt = PIKE_ODBC_RES->odbc->hstmt;
+  PIKE_ODBC_RES->odbc->hstmt = SQL_NULL_HSTMT;
+  
+  pop_n_elems(args);
+ 
+  if (PIKE_ODBC_RES->hstmt == SQL_NULL_HSTMT) {
+    free_object(PIKE_ODBC_RES->obj);
+    PIKE_ODBC_RES->odbc = NULL;
+    PIKE_ODBC_RES->obj = NULL;
+    error("odbc_result(): No result to clone\n");
+  }
+  PIKE_ODBC_RES->num_fields = PIKE_ODBC_RES->odbc->num_fields;
+  PIKE_ODBC_RES->num_rows = PIKE_ODBC_RES->odbc->affected_rows;
+  
+  odbc_fix_fields();
+}
+ 
+/* int num_rows() */
+static void f_num_rows(INT32 args)
+{
+  pop_n_elems(args);
+  push_int(PIKE_ODBC_RES->num_rows);
+}
+
+/* int num_fields() */
+static void f_num_fields(INT32 args)
+{
+  pop_n_elems(args);
+  push_int(PIKE_ODBC_RES->num_fields);
+}
+
+/* array(int|mapping(string:mixed)) fetch_fields() */
+static void f_fetch_fields(INT32 args)
+{
+  pop_n_elems(args);
+
+  PIKE_ODBC_RES->fields->refs++;
+  push_array(PIKE_ODBC_RES->fields);
+}
+ 
+/* int|array(string|float|int) fetch_row() */
+static void f_fetch_row(INT32 args)
+{
+  int i;
+  RETCODE code;
+ 
+  pop_n_elems(args);
+
+  code = SQLFetch(PIKE_ODBC_RES->hstmt);
+  
+  if (code == SQL_NO_DATA_FOUND) {
+    /* No rows left in result */
+    push_int(0);
+  } else {
+    odbc_check_error("odbc->fetch_row", "Couldn't fetch row",
+		     code, NULL);
+ 
+    for (i=0; i < PIKE_ODBC_RES->num_fields; i++) {
+      if (PIKE_ODBC_RES->field_info[i].len != SQL_NULL_DATA) {
+	switch (PIKE_ODBC_RES->field_info[i].type) {
+	case T_INT:
+	  push_int(*((int *)PIKE_ODBC_RES->field_info[i].buf));
+	  break;
+	case T_FLOAT:
+	  push_float(*((double *)PIKE_ODBC_RES->field_info[i].buf));
+	  break;
+	case T_STRING:
+	  push_string(make_shared_binary_string(PIKE_ODBC_RES->field_info[i].buf,
+						PIKE_ODBC_RES->field_info[i].len));
+	  break;
+	default:
+	  error("odbc->fetch_row(): Internal error: Unknown type (%d)\n",
+		PIKE_ODBC_RES->field_info[i].type);
+	  break;
+	}
+      } else {
+	/* NULL */
+	push_int(0);
+      }
+    }
+    f_aggregate(PIKE_ODBC_RES->num_fields);
+  }
+}
+ 
+
+ 
+/*
+ * Module linkage
+ */
+ 
+void init_odbc_res_programs(void)
+{
+  /*
+   * start_new_program();
+   *
+   * add_storage();
+   *
+   * add_function();
+   * add_function();
+   * ...
+   *
+   * set_init_callback();
+   * set_exit_callback();
+   *
+   * program = end_c_program();
+   * program->refs++;
+   *
+   */
+ 
+  start_new_program();
+  add_storage(sizeof(struct precompiled_odbc_result));
+ 
+  add_function("create", f_create, "function(object:void)", ID_PUBLIC);
+  add_function("num_rows", f_num_rows, "function(void:int)", ID_PUBLIC);
+  add_function("num_fields", f_num_fields, "function(void:int)", ID_PUBLIC);
+#ifdef SUPPORT_FIELD_SEEK
+  add_function("field_seek", f_field_seek, "function(int:void)", ID_PUBLIC);
+#endif /* SUPPORT_FIELD_SEEK */
+  add_function("eof", f_eof, "function(void:int)", ID_PUBLIC);
+#ifdef SUPPORT_FIELD_SEEK
+  add_function("fetch_field", f_fetch_field, "function(void:int|mapping(string:mixed))", ID_PUBLIC);
+#endif /* SUPPORT_FIELD_SEEK */
+  add_function("fetch_fields", f_fetch_fields, "function(void:array(int|mapping(string:mixed)))", ID_PUBLIC);
+  add_function("seek", f_seek, "function(int:void)", ID_PUBLIC);
+  add_function("fetch_row", f_fetch_row, "function(void:int|array(string|int|float))", ID_PUBLIC);
+ 
+  set_init_callback(init_res_struct);
+  set_exit_callback(exit_res_struct);
+ 
+  odbc_result_program = end_program();
+  add_program_constant("odbc_result",odbc_result_program, 0);
+}
+ 
+void exit_odbc_res(void)
+{
+  if (odbc_result_program) {
+    free_program(odbc_result_program);
+    odbc_result_program = NULL;
+  }
+}
+
+#endif /* HAVE_ODBC */
+
diff --git a/src/modules/Odbc/precompiled_odbc.h b/src/modules/Odbc/precompiled_odbc.h
new file mode 100644
index 0000000000000000000000000000000000000000..4dda0ed30dd9b4390531830f266b1acdda70d4ec
--- /dev/null
+++ b/src/modules/Odbc/precompiled_odbc.h
@@ -0,0 +1,79 @@
+/*
+ * $Id: precompiled_odbc.h,v 1.1 1997/03/10 19:01:54 grubba Exp $
+ *
+ * Pike interface to ODBC compliant databases.
+ *
+ * Henrik Grubbstr�m
+ */
+
+#ifndef PIKE_PRECOMPILED_ODBC_H
+#define PIKE_PRECOMPILED_ODBC_H
+
+/*
+ * Includes
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <qeodbc.h>
+#ifdef HAVE_SQL_H
+#include <sql.h>
+#endif /* HAVE_SQL_H */
+#ifdef HAVE_SQLEXT_H
+#include <sqlext.h>
+#endif /* HAVE_SQLEXT_H */
+
+/*
+ * Globals
+ */
+
+extern struct program *odbc_program;
+extern struct program *odbc_result_program;
+
+/*
+ * Structures
+ */
+
+struct field_info {
+  int type;		/* Pike-type */
+  SDWORD len;
+  int size;		/* Size of buffer */
+  void *buf;
+};
+
+struct precompiled_odbc {
+  HENV henv;
+  HDBC hdbc;
+  HSTMT hstmt;
+  SDWORD num_fields;
+  SWORD affected_rows;
+  struct pike_string *last_error;
+};
+
+struct precompiled_odbc_result {
+  struct object *obj;
+  struct precompiled_odbc *odbc;
+  HSTMT hstmt;
+  int num_fields;
+  int num_rows;
+  struct array *fields;
+  struct field_info *field_info;
+};
+
+/*
+ * Defines
+ */
+
+#define PIKE_ODBC	((struct precompiled_odbc *)(fp->current_storage))
+#define PIKE_ODBC_RES	((struct precompiled_odbc_result *)(fp->current_storage))
+
+/*
+ * Prototypes
+ */
+volatile void odbc_error(const char *fun, const char *msg,
+			 struct precompiled_odbc *odbc, HSTMT hstmt,
+			 RETCODE code, void (*clean)(void));
+
+#endif /* PIKE_PRECOMPILED_ODBC_H */