diff --git a/.gitattributes b/.gitattributes
index 940945cda224b6df556eb0d00974ae1cc6b5602c..107e4b7e572c8c6374ec9e7c71cbfbfde38a2b08 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -47,6 +47,10 @@ testfont binary
 /src/modules/image/pnm.c foreign_ident
 /src/modules/image/quant.c foreign_ident
 /src/modules/image/togif.c foreign_ident
+/src/modules/mysql/Makefile.src foreign_ident
+/src/modules/mysql/mysql.c foreign_ident
+/src/modules/mysql/precompiled_mysql.h foreign_ident
+/src/modules/mysql/result.c foreign_ident
 /src/modules/pipe/pipe.c foreign_ident
 /src/modules/sprintf/sprintf.c foreign_ident
 /src/modules/ssleay/ssleay.c foreign_ident
diff --git a/src/modules/mysql/Makefile.src b/src/modules/mysql/Makefile.src
new file mode 100644
index 0000000000000000000000000000000000000000..25f66ccd26230584c34c4f906e062c853daeb2ae
--- /dev/null
+++ b/src/modules/mysql/Makefile.src
@@ -0,0 +1,32 @@
+#
+# $Id: Makefile.src,v 1.1 1996/12/28 18:51:39 grubba Exp $
+#
+
+SRCDIR=@srcdir@
+VPATH=@srcdir@:@srcdir@/../..:../..
+PREFLAGS=$(DEFINES) @DEFS@ -I. -I$(SRCDIR) -I$(SRCDIR)/../.. -I../.. @CPPFLAGS@
+
+CFLAGS=$(PREFLAGS) $(OTHERFLAGS)
+
+OBJS=mysql.o result.o
+
+all: mysql.a linker_options
+
+mysql.a: $(OBJS)
+	-rm -f mysql.a
+	ar cq mysql.a $(OBJS)
+	-@RANLIB@ mysql.a
+
+linker_options: Makefile
+	echo >linker_options "@LDFLAGS@ @LIBS@"
+
+verify:
+	@echo No tests for the mysql-module.
+
+verbose_verify: verify
+
+clean:
+	-rm -f *.o *.a
+
+depend:
+	gcc -MM $(PREFLAGS) $(SRCDIR)/*.c | $(TMP_BINDIR)/fixdepends.sh $(SRCDIR)
diff --git a/src/modules/mysql/configure.in b/src/modules/mysql/configure.in
new file mode 100644
index 0000000000000000000000000000000000000000..5c481f62f9d409409d766539b0c35affa9eacbe9
--- /dev/null
+++ b/src/modules/mysql/configure.in
@@ -0,0 +1,62 @@
+AC_INIT(mysql.c)
+
+AC_PROG_CC
+AC_PROG_RANLIB
+AC_SUBST(RANLIB)
+
+AC_ARG_WITH(mysql,  [  --without-mysql       no support for the Mysql database],[],[with_mysql=yes])
+
+if test x$with_mysql = xyes; then
+  AC_MSG_CHECKING(Checking for existance of Mysql)
+  
+  AC_CACHE_VAL(pike_cv_mysql_exists,
+  [
+    if test -d /usr/local/mysql/. ; then
+      pike_cv_mysql_exists="yes"
+    else
+      pike_cv_mysql_exists="no"
+    fi
+  ])
+  
+  AC_MSG_RESULT($pike_cv_mysql_exists)
+  
+  if test x$pike_cv_mysql_exists = xyes; then
+  
+    if test -d /usr/local/mysql/mach-lib-thread/. ; then
+      echo Added /usr/local/mysql/mach-lib-thread to the library search path.
+      LDFLAGS="-L/usr/local/mysql/mach-lib-thread ${LDFLAGS}"
+    fi
+
+    if test -d /usr/local/mysql/include ; then
+      echo Added /usr/local/mysql/include to the include search path.
+      CPPFLAGS="-I/usr/local/mysql/include ${CPPFLAGS}"
+    fi
+
+    pike_cv_mysql="yes"
+
+    # System libs which might be needed
+
+    AC_CHECK_LIB(pthread, pthread_self, [], [])
+    AC_CHECK_LIB(pthreads, pthread_self, [], [])
+    AC_CHECK_LIB(socket, socket, [], [])
+    AC_CHECK_LIB(nsl, gethostbyname, [], [])
+    AC_CHECK_LIB(m, floor, [], [])
+
+    # Mysql libs
+
+    AC_CHECK_LIB(strings, bchange, [], [ pike_cv_mysql="no" ])
+    AC_CHECK_LIB(dbug, _db_doprnt_, [], [ pike_cv_mysql="no" ])
+    AC_CHECK_LIB(mysys, my_init, [], [ pike_cv_mysql="no" ])
+    AC_CHECK_LIB(mysql, mysql_connect, [], [ pike_cv_mysql="no" ])
+
+    AC_MSG_CHECKING(Supported version of Mysql)
+
+    AC_MSG_RESULT($pike_cv_mysql)
+
+    if test x$pike_cv_mysql = xyes; then
+      AC_DEFINE(HAVE_MYSQL)
+    fi
+  fi
+fi
+
+AC_OUTPUT(Makefile,echo FOO >stamp-h )
diff --git a/src/modules/mysql/mysql.c b/src/modules/mysql/mysql.c
new file mode 100644
index 0000000000000000000000000000000000000000..0d3a0a428000947bc0babebcb705d71297968eef
--- /dev/null
+++ b/src/modules/mysql/mysql.c
@@ -0,0 +1,461 @@
+/*
+ * $Id: mysql.c,v 1.1 1996/12/28 18:51:41 grubba Exp $
+ *
+ * SQL database functionality for Pike
+ *
+ * Henrik Grubbstr�m 1996-12-21
+ */
+
+#ifdef HAVE_MYSQL
+
+/*
+ * Includes
+ */
+
+/* From the mysql-dist */
+#ifndef MYSQL_MYSQL_H
+#define MYSQL_MYSQL_H
+#include <mysql.h>
+#endif
+
+/* dynamic_buffer.h contains a conflicting typedef for string
+ * we don't use any dynamic buffers, so we have this work-around
+ */
+#define DYNAMIC_BUFFER_H
+typedef struct dynamic_buffer_s dynamic_buffer;
+
+#endif /* HAVE_MYSQL */
+
+/* From the Pike-dist */
+#include <svalue.h>
+#include <object.h>
+#include <stralloc.h>
+#include <interpret.h>
+#include <port.h>
+#include <error.h>
+#include <las.h>
+
+/* Local includes */
+#include "precompiled_mysql.h"
+
+/* System includes */
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+
+#ifdef HAVE_MYSQL
+
+/*
+ * Globals
+ */
+
+struct program *mysql_program = NULL;
+
+/*
+ * Functions
+ */
+
+/*
+ * State maintenance
+ */
+
+static void init_mysql_struct(struct object *o)
+{
+  MEMSET(PIKE_MYSQL, 0, sizeof(struct precompiled_mysql));
+}
+
+static void exit_mysql_struct(struct object *o)
+{
+  if (PIKE_MYSQL->last_result) {
+    mysql_free_result(PIKE_MYSQL->last_result);
+    PIKE_MYSQL->last_result = NULL;
+  }
+  if (PIKE_MYSQL->socket) {
+    mysql_close(PIKE_MYSQL->socket);
+    PIKE_MYSQL->socket = NULL;
+  }
+}
+
+/*
+ * Methods
+ */
+
+/* void create(string|void host, string|void database, string|void password) */
+static void f_create(INT32 args)
+{
+  char *host = NULL;
+  char *database = NULL;
+  char *password = NULL;
+
+  if (args >= 1) {
+    if (sp[-args].type != T_STRING) {
+      error("Bad argument 1 to mysql()\n");
+    }
+    if (sp[-args].u.string->len) {
+      host = sp[-args].u.string->str;
+    }
+
+    if (args >= 2) {
+      if (sp[1-args].type != T_STRING) {
+	error("Bad argument 2 to mysql()\n");
+      }
+      if (sp[-args].u.string->len) {
+	database = sp[1-args].u.string->str;
+      }
+
+      if (args >= 3) {
+	if (sp[2-args].type != T_STRING) {
+	  error("Bad argument 3 to mysql()\n");
+	}
+	if (sp[-args].u.string->len) {
+	  password = sp[2-args].u.string->str;
+	}
+      }
+    }
+  }
+
+  pop_n_elems(args);
+
+  if (!(PIKE_MYSQL->socket = mysql_connect(&PIKE_MYSQL->mysql, host,
+					   0, password))) {
+    error("mysql(): Couldn't connect to SQL-server\n");
+  }
+  if (database && (mysql_select_db(PIKE_MYSQL->socket, database) < 0)) {
+    mysql_close(PIKE_MYSQL->socket);
+    PIKE_MYSQL->socket = NULL;
+    error("mysql(): Couldn't select database \"%s\"\n", database);
+  }
+}
+
+/* int affected_rows() */
+static void f_affected_rows(INT32 args)
+{
+  pop_n_elems(args);
+  push_int(mysql_affected_rows(PIKE_MYSQL->socket));
+}
+
+/* int insert_id() */
+static void f_insert_id(INT32 args)
+{
+  pop_n_elems(args);
+  push_int(mysql_insert_id(PIKE_MYSQL->socket));
+}
+
+/* string error() */
+static void f_error(INT32 args)
+{
+  pop_n_elems(args);
+  push_text(mysql_error(PIKE_MYSQL->socket));
+}
+
+/* void select_db(string database) */
+static void f_select_db(INT32 args)
+{
+  if (!args) {
+    error("Too few arguments to mysql->select_db()\n");
+  }
+  if (sp[-args].type != T_STRING) {
+    error("Bad argument 1 to mysql->select_db()\n");
+  }
+
+  if (mysql_select_db(PIKE_MYSQL->socket, sp[-args].u.string->str) < 0) {
+    error("mysql->select_db(): Couldn't select database \"%s\"\n",
+	  sp[-args].u.string->str);
+  }
+
+  pop_n_elems(args);
+}
+
+/* object(mysql_result) query(string q) */
+static void f_query(INT32 args)
+{
+  if (!args) {
+    error("Too few arguments to mysql->query()\n");
+  }
+  if (sp[-args].type != T_STRING) {
+    error("Bad argument 1 to mysql->query()\n");
+  }
+  if (mysql_query(PIKE_MYSQL->socket, sp[-args].u.string->str) < 0) {
+    error("mysql->query(): Query \"%s\" failed\n",
+	  sp[-args].u.string->str);
+  }
+
+  if (!(PIKE_MYSQL->last_result = mysql_store_result(PIKE_MYSQL->socket))) {
+    error("mysql->query(): Couldn't create result for query\n");
+  }
+
+  pop_n_elems(args);
+
+  push_object(fp->current_object);
+  fp->current_object->refs++;
+
+  push_object(clone(mysql_result_program, 1));
+}
+
+/* void create_db(string database) */
+static void f_create_db(INT32 args)
+{
+  if (!args) {
+    error("Too few arguments to mysql->create_db()\n");
+  }
+  if (sp[-args].type != T_STRING) {
+    error("Bad argument 1 to mysql->create_db()\n");
+  }
+  if (sp[-args].u.string->len > 127) {
+    error("Database name \"%s\" is too long (max 127 characters)\n",
+	  sp[-args].u.string->str);
+  }
+  if (mysql_create_db(PIKE_MYSQL->socket, sp[-args].u.string->str) < 0) {
+    error("mysql->create_db(): Creation of database \"%s\" failed\n",
+	  sp[-args].u.string->str);
+  }
+  pop_n_elems(args);
+}
+
+/* void drop_db(string database) */
+static void f_drop_db(INT32 args)
+{
+  if (!args) {
+    error("Too few arguments to mysql->drop_db()\n");
+  }
+  if (sp[-args].type != T_STRING) {
+    error("Bad argument 1 to mysql->drop_db()\n");
+  }
+  if (sp[-args].u.string->len > 127) {
+    error("Database name \"%s\" is too long (max 127 characters)\n",
+	  sp[-args].u.string->str);
+  }
+  if (mysql_drop_db(PIKE_MYSQL->socket, sp[-args].u.string->str) < 0) {
+    error("mysql->drop_db(): Drop of database \"%s\" failed\n",
+	  sp[-args].u.string->str);
+  }
+  pop_n_elems(args);
+}
+
+/* void shutdown() */
+static void f_shutdown(INT32 args)
+{
+  if (mysql_shutdown(PIKE_MYSQL->socket) < 0) {
+    error("mysql->shutdown(): Shutdown failed\n");
+  }
+
+  pop_n_elems(args);
+}
+
+/* void reload() */
+static void f_reload(INT32 args)
+{
+  if (mysql_reload(PIKE_MYSQL->socket) < 0) {
+    error("mysql->reload(): Reload failed\n");
+  }
+
+  pop_n_elems(args);
+}
+
+/* string statistics() */
+static void f_statistics(INT32 args)
+{
+  pop_n_elems(args);
+
+  push_text(mysql_stat(PIKE_MYSQL->socket));
+}
+
+/* string server_info() */
+static void f_server_info(INT32 args)
+{
+  pop_n_elems(args);
+
+  push_text(mysql_get_server_info(PIKE_MYSQL->socket));
+}
+
+/* string host_info() */
+static void f_host_info(INT32 args)
+{
+  pop_n_elems(args);
+
+  push_text(mysql_get_host_info(PIKE_MYSQL->socket));
+}
+
+/* int protocol_info() */
+static void f_protocol_info(INT32 args)
+{
+  pop_n_elems(args);
+
+  push_int(mysql_get_proto_info(PIKE_MYSQL->socket));
+}
+
+/* object(mysql_res) list_dbs(void|string wild) */
+static void f_list_dbs(INT32 args)
+{
+  char *wild = "*";
+
+  if (args) {
+    if (sp[-args].type != T_STRING) {
+      error("Bad argument 1 to mysql->list_dbs()\n");
+    }
+    if (sp[-args].u.string->len > 80) {
+      error("Wildcard \"%s\" is too long (max 80 characters)\n",
+	    sp[-args].u.string->str);
+    }
+    wild = sp[-args].u.string->str;
+  }
+  pop_n_elems(args);
+
+  if (!(PIKE_MYSQL->last_result = mysql_list_dbs(PIKE_MYSQL->socket, wild))) {
+    error("mysql->list_dbs(): Cannot list databases: %s\n",
+	  mysql_error(PIKE_MYSQL->socket));
+  }
+
+  push_object(fp->current_object);
+  fp->current_object->refs++;
+
+  push_object(clone(mysql_result_program, 1));
+}
+
+/* object(mysql_res) list_tables(void|string wild) */
+static void f_list_tables(INT32 args)
+{
+  char *wild = "*";
+
+  if (args) {
+    if (sp[-args].type != T_STRING) {
+      error("Bad argument 1 to mysql->list_tables()\n");
+    }
+    if (sp[-args].u.string->len > 80) {
+      error("Wildcard \"%s\" is too long (max 80 characters)\n",
+	    sp[-args].u.string->str);
+    }
+    wild = sp[-args].u.string->str;
+  }
+  pop_n_elems(args);
+
+  PIKE_MYSQL->last_result = mysql_list_tables(PIKE_MYSQL->socket, wild);
+
+  push_object(fp->current_object);
+  fp->current_object->refs++;
+
+  push_object(clone(mysql_result_program, 1));
+}
+
+#if 0
+/* DON'T USE! */
+
+/* object(mysql_res) list_fields(void|string wild) */
+static void f_list_fields(INT32 args)
+{
+  char *wild = "*";
+
+  if (args) {
+    if (sp[-args].type != T_STRING) {
+      error("Bad argument 1 to mysql->list_fields()\n");
+    }
+    if (sp[-args].u.string->len > 80) {
+      error("Wildcard \"%s\" is too long (max 80 characters)\n",
+	    sp[-args].u.string->str);
+    }
+    wild = sp[-args].u.string->str;
+  }
+  pop_n_elems(args);
+
+  PIKE_MYSQL->last_result = mysql_list_fields(PIKE_MYSQL->socket, wild);
+
+  push_object(fp->current_object);
+  fp->current_object->refs++;
+
+  push_object(clone(mysql_result_program, 1));
+}
+
+#endif /* 0 */
+
+/* object(mysql_res) list_processes(void|string wild) */
+static void f_list_processes(INT32 args)
+{
+  pop_n_elems(args);
+
+  PIKE_MYSQL->last_result = mysql_list_processes(PIKE_MYSQL->socket);
+
+  push_object(fp->current_object);
+  fp->current_object->refs++;
+
+  push_object(clone(mysql_result_program, 1));
+}
+
+#endif /* HAVE_MYSQL */
+
+/*
+ * Module linkage
+ */
+
+void init_mysql_efuns(void)
+{
+#ifdef HAVE_MYSQL
+  init_mysql_res_efuns();
+#endif /* HAVE_MYSQL */
+}
+
+void init_mysql_programs(void)
+{
+#ifdef HAVE_MYSQL
+  /*
+   * 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_mysql));
+
+  add_function("error", f_error, "function(void:string)", OPT_EXTERNAL_DEPEND);
+  add_function("create", f_create, "function(string|void, string|void, string|void:void)", OPT_SIDE_EFFECT);
+  add_function("affected_rows", f_affected_rows, "function(void:int)", OPT_EXTERNAL_DEPEND);
+  add_function("insert_id", f_insert_id, "function(void:int)", OPT_EXTERNAL_DEPEND);
+  add_function("select_db", f_select_db, "function(string:void)", OPT_SIDE_EFFECT);
+  add_function("query", f_query, "function(string:object)", OPT_EXTERNAL_DEPEND);
+  add_function("create_db", f_create_db, "function(string:void)", OPT_SIDE_EFFECT);
+  add_function("drop_db", f_drop_db, "function(string:void)", OPT_SIDE_EFFECT);
+  add_function("shutdown", f_shutdown, "function(void:void)", OPT_SIDE_EFFECT);
+  add_function("reload", f_reload, "function(void:void)", OPT_SIDE_EFFECT);
+  add_function("statistics", f_statistics, "function(void:string)", OPT_EXTERNAL_DEPEND);
+  add_function("server_info", f_server_info, "function(void:string)", OPT_EXTERNAL_DEPEND);
+  add_function("host_info", f_host_info, "function(void:string)", OPT_EXTERNAL_DEPEND);
+  add_function("protocol_info", f_protocol_info, "function(void:int)", OPT_EXTERNAL_DEPEND);
+  add_function("list_dbs", f_list_dbs, "function(void|string:object)", OPT_EXTERNAL_DEPEND);
+  add_function("list_tables", f_list_tables, "function(void|string:object)", OPT_EXTERNAL_DEPEND);
+  add_function("list_processes", f_list_processes, "function(void|string:object)", OPT_EXTERNAL_DEPEND);
+
+  set_init_callback(init_mysql_struct);
+  set_exit_callback(exit_mysql_struct);
+
+  mysql_program = end_c_program("/precompiled/mysql");
+  mysql_program->refs++;
+
+  init_mysql_res_programs();
+#endif /* HAVE_MYSQL */
+}
+
+void exit_mysql(void)
+{
+#ifdef HAVE_MYSQL
+  exit_mysql_res();
+
+  if (mysql_program) {
+    free_program(mysql_program);
+    mysql_program = NULL;
+  }
+#endif /* HAVE_MYSQL */
+}
+
diff --git a/src/modules/mysql/precompiled_mysql.h b/src/modules/mysql/precompiled_mysql.h
new file mode 100644
index 0000000000000000000000000000000000000000..75fba7ca2e401c35ca72d1dc262b6a0686b5654d
--- /dev/null
+++ b/src/modules/mysql/precompiled_mysql.h
@@ -0,0 +1,61 @@
+/*
+ * $Id: precompiled_mysql.h,v 1.1 1996/12/28 18:51:41 grubba Exp $
+ *
+ * SQL database connectivity for Pike
+ *
+ * Henrik Grubbstr�m 1996-12-21
+ */
+
+#ifndef PRECOMPILED_MYSQL_H
+#define PRECOMPILED_MYSQL_H
+
+/*
+ * Includes
+ */
+
+/* From the mysql-dist */
+#ifndef MYSQL_MYSQL_H
+#define MYSQL_MYSQL_H
+#include <mysql.h>
+#endif
+
+/* From the Pike-dist */
+
+/*
+ * Structures
+ */
+
+struct precompiled_mysql {
+  MYSQL		mysql, *socket;
+  MYSQL_RES	*last_result;	/* UGLY way to pass arguments to create() */
+};
+
+struct precompiled_mysql_result {
+  MYSQL_RES	*result;
+};
+
+/*
+ * Defines
+ */
+
+#define PIKE_MYSQL	((struct precompiled_mysql *)(fp->current_storage))
+#define PIKE_MYSQL_RES	((struct precompiled_mysql_result *)(fp->current_storage))
+
+/*
+ * Globals
+ */
+
+extern struct program *mysql_program;
+extern struct program *mysql_result_program;
+
+/*
+ * Prototypes
+ */
+
+/* From result.c */
+
+void init_mysql_res_efuns(void);
+void init_mysql_res_programs(void);
+void exit_mysql_res(void);
+
+#endif /* PRECOMPILED_MYSQL_H */
diff --git a/src/modules/mysql/result.c b/src/modules/mysql/result.c
new file mode 100644
index 0000000000000000000000000000000000000000..b0008b9eea885ee6ff432255f520bf5e22101693
--- /dev/null
+++ b/src/modules/mysql/result.c
@@ -0,0 +1,329 @@
+/*
+ * $Id: result.c,v 1.1 1996/12/28 18:51:42 grubba Exp $
+ *
+ * mysql query result
+ *
+ * Henrik Grubbstr�m 1996-12-21
+ */
+
+#ifdef HAVE_MYSQL
+/*
+ * Includes
+ */
+
+/* From the mysql-dist */
+#ifndef MYSQL_MYSQL_H
+#define MYSQL_MYSQL_H
+#include <mysql.h>
+#endif
+
+/* dynamic_buffer.h contains a conflicting typedef for string
+ * we don't use any dynamic buffers, so we have this work-around
+ */
+#define DYNAMIC_BUFFER_H
+typedef struct dynamic_buffer_s dynamic_buffer;
+
+/* From the Pike-dist */
+#include <svalue.h>
+#include <mapping.h>
+#include <object.h>
+#include <program.h>
+#include <stralloc.h>
+#include <interpret.h>
+#include <error.h>
+#include <builtin_functions.h>
+#include <las.h>
+
+/* Local includes */
+#include "precompiled_mysql.h"
+
+/* System includes */
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_MEMORY_H
+#include <memory.h>
+#endif
+
+/*
+ * Globals
+ */
+
+struct program *mysql_result_program = NULL;
+
+/*
+ * Functions
+ */
+
+/*
+ * State maintenance
+ */
+
+static void init_res_struct(struct object *o)
+{
+  memset(PIKE_MYSQL_RES, 0, sizeof(struct precompiled_mysql_result));
+}
+
+static void exit_res_struct(struct object *o)
+{
+  if (PIKE_MYSQL_RES->result) {
+    mysql_free_result(PIKE_MYSQL_RES->result);
+    PIKE_MYSQL_RES->result = NULL;
+  }
+}
+
+/*
+ * Methods
+ */
+
+/* void create(object(mysql)) */
+static void f_create(INT32 args)
+{
+  if (!args) {
+    error("Too few arguments to mysql_result()\n");
+  }
+  if ((sp[-args].type != T_OBJECT) ||
+      (sp[-args].u.object->prog != mysql_program)) {
+    error("Bad argument 1 to mysql_result()\n");
+  }
+
+  PIKE_MYSQL_RES->result = ((struct precompiled_mysql *)sp[-args].u.object->storage)->last_result;
+  ((struct precompiled_mysql *)sp[-args].u.object->storage)->last_result = NULL;
+  
+  pop_n_elems(args);
+
+  if (!PIKE_MYSQL_RES->result) {
+    error("mysql_result(): No result to clone\n");
+  }
+}
+
+/* int num_rows() */
+static void f_num_rows(INT32 args)
+{
+  pop_n_elems(args);
+  push_int(mysql_num_rows(PIKE_MYSQL_RES->result));
+}
+
+/* int num_fields() */
+static void f_num_fields(INT32 args)
+{
+  pop_n_elems(args);
+  push_int(mysql_num_fields(PIKE_MYSQL_RES->result));
+}
+
+/* void field_seek(int fieldno) */
+static void f_field_seek(INT32 args)
+{
+  if (!args) {
+    error("Too few arguments to mysql->field_seek()\n");
+  }
+  if (sp[-args].type != T_INT) {
+    error("Bad argument 1 to mysql->field_seek()\n");
+  }
+  mysql_field_seek(PIKE_MYSQL_RES->result, sp[-args].u.integer);
+  pop_n_elems(args);
+}
+
+/* int eof() */
+static void f_eof(INT32 args)
+{
+  pop_n_elems(args);
+  push_int(mysql_eof(PIKE_MYSQL_RES->result));
+}
+
+/* array(int|mapping(string:mixed)) fetch_field() */
+static void f_fetch_field(INT32 args)
+{
+  unsigned int i;
+
+  pop_n_elems(args);
+
+  for (i=0; i < mysql_num_fields(PIKE_MYSQL_RES->result); i++) {
+    MYSQL_FIELD *field = mysql_fetch_field(PIKE_MYSQL_RES->result);
+
+    if (field) {
+      push_text("name"); push_text(field->name);
+      push_text("table"); push_text(field->table);
+      push_text("def");
+      if (field->def) {
+	push_text(field->def);
+      } else {
+	push_int(0);
+      }
+      push_text("type");
+      switch(field->type) {
+      case FIELD_TYPE_DECIMAL:
+	push_text("decimal");
+	break;
+      case FIELD_TYPE_CHAR:
+	push_text("char");
+	break;
+      case FIELD_TYPE_SHORT:
+	push_text("short");
+	break;
+      case FIELD_TYPE_LONG:
+	push_text("long");
+	break;
+      case FIELD_TYPE_FLOAT:
+	push_text("float");
+	break;
+      case FIELD_TYPE_DOUBLE:
+	push_text("double");
+	break;
+      case FIELD_TYPE_NULL:
+	push_text("null");
+	break;
+      case FIELD_TYPE_TIME:
+	push_text("time");
+	break;
+      case FIELD_TYPE_LONGLONG:
+	push_text("longlong");
+	break;
+      case FIELD_TYPE_INT24:
+	push_text("int24");
+	break;
+      case FIELD_TYPE_TINY_BLOB:
+	push_text("tiny blob");
+	break;
+      case FIELD_TYPE_MEDIUM_BLOB:
+	push_text("medium blob");
+	break;
+      case FIELD_TYPE_LONG_BLOB:
+	push_text("long blob");
+	break;
+      case FIELD_TYPE_BLOB:
+	push_text("blob");
+	break;
+      case FIELD_TYPE_VAR_STRING:
+	push_text("var string");
+	break;
+      case FIELD_TYPE_STRING:
+	push_text("string");
+	break;
+      default:
+	push_text("unknown");
+	break;
+      }
+      push_text("length"); push_int(field->length);
+      push_text("max_length"); push_int(field->max_length);
+      push_text("flags"); push_int(field->flags);		/*************/
+      push_text("decimals"); push_int(field->decimals);
+      
+      f_aggregate_mapping(8*2);
+    } else {
+      /*
+       * Should this be an error?
+       */
+      push_int(0);
+    }
+  }
+  f_aggregate(mysql_num_fields(PIKE_MYSQL_RES->result));
+  mysql_field_seek(PIKE_MYSQL_RES->result, 0);
+}
+
+/* void seek(int row) */
+static void f_seek(INT32 args)
+{
+  if (!args) {
+    error("Too few arguments to mysql_result->seek()\n");
+  }
+  if (sp[-args].type != T_INT) {
+    error("Bad argument 1 to mysql_result->seek()\n");
+  }
+  if (sp[-args].u.integer < 0) {
+    error("Negative argument 1 to mysql_result->seek()\n");
+  }
+  mysql_data_seek(PIKE_MYSQL_RES->result, sp[-args].u.integer);
+
+  pop_n_elems(args);
+}
+
+/* void|array(string|int) fetch_row() */
+static void f_fetch_row(INT32 args)
+{
+  /* I have no idea how this works.
+   *
+   * Stolen from Mysql.xs
+   */
+  MYSQL_ROW row = mysql_fetch_row(PIKE_MYSQL_RES->result);
+
+  pop_n_elems(args);
+
+  if (row) {
+    int num_fields, i;
+
+    /* Mysql.xs does a mysql_field_seek(result, 0) here,
+     * but that seems to be a NOOP.
+     */
+    if ((num_fields = mysql_num_fields(PIKE_MYSQL_RES->result)) <= 0) {
+      num_fields = 1;
+    }
+    for (i=0; i < num_fields; i++) {
+      /* Mysql.xs does a mysql_fetch_field(result) here,
+       * but throws away the result.
+       */
+      if (row[i]) {
+	push_text(row[i]);
+      } else {
+	push_int(0);
+      }
+    }
+    f_aggregate(num_fields);
+  }
+}
+
+/*
+ * Module linkage
+ */
+
+void init_mysql_res_efuns(void)
+{
+}
+
+void init_mysql_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_mysql_result));
+
+  add_function("create", f_create, "function(object:void)", OPT_SIDE_EFFECT);
+  add_function("num_rows", f_num_rows, "function(void:int)", OPT_EXTERNAL_DEPEND);
+  add_function("num_fields", f_num_fields, "function(void:int)", OPT_EXTERNAL_DEPEND);
+  add_function("field_seek", f_field_seek, "function(int:void)", OPT_SIDE_EFFECT);
+  add_function("eof", f_eof, "function(void:int)", OPT_EXTERNAL_DEPEND);
+  add_function("fetch_field", f_fetch_field, "function(void:array(int|mapping(string:mixed)))", OPT_EXTERNAL_DEPEND);
+  add_function("seek", f_seek, "function(int:void)", OPT_SIDE_EFFECT);
+  add_function("fetch_row", f_fetch_row, "function(void:void|array(string|int))", OPT_EXTERNAL_DEPEND|OPT_SIDE_EFFECT);
+
+  set_init_callback(init_res_struct);
+  set_exit_callback(exit_res_struct);
+
+  mysql_result_program = end_c_program("/precompiled/mysql_res");
+  mysql_result_program->refs++;
+}
+
+void exit_mysql_res(void)
+{
+  if (mysql_result_program) {
+    free_program(mysql_result_program);
+    mysql_result_program = NULL;
+  }
+}
+
+#endif /* HAVE_MYSQL */