From 25ce50da64b0b3ed4a6bb2b63fa8aa66edad764a Mon Sep 17 00:00:00 2001
From: Francesco Chemolli <li@kinkie.it>
Date: Tue, 14 Oct 1997 23:55:43 +0200
Subject: [PATCH] Msql mod 1.1 pre 6.

Rev: src/modules/Msql/COPYING:1.1.1.1
Rev: src/modules/Msql/ChangeLog:1.1.1.1
Rev: src/modules/Msql/INSTALL:1.1.1.1
Rev: src/modules/Msql/Makefile.in:1.1.1.1
Rev: src/modules/Msql/NOTES:1.1.1.1
Rev: src/modules/Msql/configure.in:1.1.1.1
Rev: src/modules/Msql/doc/msql:1.1.1.1
Rev: src/modules/Msql/msql_config.h.in:1.1.1.1
Rev: src/modules/Msql/msqlmod.c:1.1.1.1
Rev: src/modules/Msql/test_msqlmod.pike:1.1.1.1
Rev: src/modules/Msql/testsuite.in:1.1.1.1
---
 .gitattributes                     |   3 +
 src/modules/Msql/COPYING           |  17 +
 src/modules/Msql/ChangeLog         |  49 ++
 src/modules/Msql/INSTALL           |  17 +
 src/modules/Msql/Makefile.in       |  26 +
 src/modules/Msql/NOTES             |   2 +
 src/modules/Msql/configure.in      | 108 ++++
 src/modules/Msql/doc/msql          | 371 ++++++++++++++
 src/modules/Msql/msql_config.h.in  |  23 +
 src/modules/Msql/msqlmod.c         | 783 +++++++++++++++++++++++++++++
 src/modules/Msql/test_msqlmod.pike |  33 ++
 src/modules/Msql/testsuite.in      |   0
 12 files changed, 1432 insertions(+)
 create mode 100644 src/modules/Msql/COPYING
 create mode 100644 src/modules/Msql/ChangeLog
 create mode 100644 src/modules/Msql/INSTALL
 create mode 100644 src/modules/Msql/Makefile.in
 create mode 100644 src/modules/Msql/NOTES
 create mode 100644 src/modules/Msql/configure.in
 create mode 100644 src/modules/Msql/doc/msql
 create mode 100644 src/modules/Msql/msql_config.h.in
 create mode 100644 src/modules/Msql/msqlmod.c
 create mode 100755 src/modules/Msql/test_msqlmod.pike
 create mode 100644 src/modules/Msql/testsuite.in

diff --git a/.gitattributes b/.gitattributes
index c24f286a8b..e293d87cd9 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -92,6 +92,9 @@ testfont binary
 /src/modules/Image/x.c foreign_ident
 /src/modules/MIME/mime.c foreign_ident
 /src/modules/MIME/module.pmod.in foreign_ident
+/src/modules/Msql/configure.in foreign_ident
+/src/modules/Msql/msql_config.h.in foreign_ident
+/src/modules/Msql/msqlmod.c foreign_ident
 /src/modules/Mysql/Makefile.in foreign_ident
 /src/modules/Mysql/acconfig.h foreign_ident
 /src/modules/Mysql/configure.in foreign_ident
diff --git a/src/modules/Msql/COPYING b/src/modules/Msql/COPYING
new file mode 100644
index 0000000000..ae18060d52
--- /dev/null
+++ b/src/modules/Msql/COPYING
@@ -0,0 +1,17 @@
+This code is (c) 1996,1997 Francesco Chemolli <kinkie@comedia.it>
+
+It may be copied and distributed freely under the terms of the GNU
+General Public License, version 2.
+You should have received it along with your pike sources.
+
+Use this software at your own risk. There's NO WARRANTY, EITHER EXPLICIT
+OR IMPLICIT, that this software won't disrupt your work or blow your
+computer. If this can console you, I'm using it, and my computer is still
+intact :).
+
+If you use this software and find it useful, please take the time to
+send me an email. This will definitely help me keep my overinflated ego 
+above guard level, and encourage me improve this software.
+
+  Hoping to be useful,
+	  Francesco Chemolli
diff --git a/src/modules/Msql/ChangeLog b/src/modules/Msql/ChangeLog
new file mode 100644
index 0000000000..ee57e1a2d0
--- /dev/null
+++ b/src/modules/Msql/ChangeLog
@@ -0,0 +1,49 @@
+Mon Jun 30 13:57:46 1997  Francesco Chemolli  <kinkie@ai-chan>
+
+	* configure.in, configure:
+	Configure fixes which allow much more flexibility in configuration and
+	maintainance.
+
+Sun Jun 29 20:05:50 1997  Francesco Chemolli  <kinkie@ai-chan>
+
+	* msqlmod.c:
+	Corretto un errore nel parsing degli argomenti in list_index()
+
+	* configure.in: Eliminati dei doppi test in configure.in
+
+Thu Jun 19 10:26:29 1997  Francesco Chemolli  <kinkie@ai-chan>
+
+	* msqlmod.c:
+	I had unavvertitedly rolled back to an older version of msqlmod.c.
+	Corrected the reinserted bugs.
+
+Fri Jun 13 21:42:48 1997  Francesco Chemolli  <kinkie@ai-chan>
+
+	* ChangeLog, INSTALL: Some documentation.
+
+	* Makefile.in, msqlmod.c:
+	Seems to work now. I guess it's time to bump the version number up...
+
+	* msqlmod.c, Makefile.in: Piccola correzione in msqlmod.c
+	Sempre alla caccia di un hook in dynamic_module_makefile
+
+	* Makefile.in:
+	Sulla strada per sistemare la compilazione... dovremmo quasi esserci.
+
+	* lib/msql.h, lib/msql.pike, configure.in, msql_config.h.in, msqlmod.c, Makefile.in, configure:
+	Compattata la distribuzione: ora installa le librerie
+	partendo dalla sola directory pike/src/modules/Msql.
+
+	* configure, dependencies: Aggiunto il file vuoto "dependencies".
+
+Wed Apr  2 11:31:44 1997  Francesco Chemolli  <kinkie@ai-chan>
+
+	* COPYING, ChangeLog, INSTALL, Makefile.in, NOTES, configure, configure.in, doc/msql, msqlmod.c, test_msqlmod.pike, testsuite.in:
+	Initial revision
+
+	* COPYING, ChangeLog, INSTALL, Makefile.in, NOTES, configure, configure.in, doc/msql, msqlmod.c, test_msqlmod.pike, testsuite.in:
+	Initial version of the Msql module for Pike/0.5b4.
+	The Makefile has to be adjusted to auto-install all necessary things,
+	but this needs investigation in the Pike makefiles structure.
+	Maybe the documentation is out of date.
+
diff --git a/src/modules/Msql/INSTALL b/src/modules/Msql/INSTALL
new file mode 100644
index 0000000000..fdc5058922
--- /dev/null
+++ b/src/modules/Msql/INSTALL
@@ -0,0 +1,17 @@
+To install this module you need to untar your pike sources, 
+and in the src/modules/ directory untar this code.
+
+Then in the src/ directory,
+./configure
+make
+
+You need to have the header file msql.h and the library libmsql.a in one of the
+'standard' locations. If you have it, and the configure-script
+doesn't find it, you can set BEFORE RUNNING CONFIGURE the environment
+variables CPPFLAGS and LDFLAGS to the flags you'd use for your compiler
+to locate the include and library files msql.h and libmsql.a
+
+Documentation is availible in the doc/ directory, it will be HTML-ized
+if you
+make html_docs
+as Msql_msql.html
diff --git a/src/modules/Msql/Makefile.in b/src/modules/Msql/Makefile.in
new file mode 100644
index 0000000000..8f110bddd9
--- /dev/null
+++ b/src/modules/Msql/Makefile.in
@@ -0,0 +1,26 @@
+SRCDIR=@srcdir@
+VPATH=@srcdir@:@srcdir@/../..:../..
+OBJS=msqlmod.o
+MODULE_LDFLAGS=@LDFLAGS@ @LIBS@
+MODULE_CPPFLAGS=@CPPFLAGS@
+MSQL_SUPPORTED=@MSQL_SUPPORTED@
+
+all: module_install
+
+module_install: include_install glue_install mod_install
+
+include_install: lib/msql.h
+	if [ x$(MSQL_SUPPORTED) = xyes ] ; then cp lib/msql.h $(TMP_LIBDIR)/include/msql.h; fi
+
+glue_install: lib/msql.pike
+	if [ x$(MSQL_SUPPORTED) = xyes ] ; then cp lib/msql.pike $(TMP_LIBDIR)/modules/Sql.pmod/msql.pike; fi
+
+mod_install:
+	if [ x$(MSQL_SUPPORTED) = xyes ] ; then make dummy; fi
+
+@dynamic_module_makefile@
+
+spotless: clean
+	-rm config.*
+
+@dependencies@
diff --git a/src/modules/Msql/NOTES b/src/modules/Msql/NOTES
new file mode 100644
index 0000000000..2f9a26d9e4
--- /dev/null
+++ b/src/modules/Msql/NOTES
@@ -0,0 +1,2 @@
+Although it seems that mSQL/2.0 has some support for server statistics,
+it's really VERY VERY primitive, so I won't add it for now.
diff --git a/src/modules/Msql/configure.in b/src/modules/Msql/configure.in
new file mode 100644
index 0000000000..0bd2e56df1
--- /dev/null
+++ b/src/modules/Msql/configure.in
@@ -0,0 +1,108 @@
+dnl $Id: configure.in,v 1.1.1.1 1997/10/14 21:55:42 grubba Exp $
+dnl (C) 1997 Francesco Chemolli <kinkie@comedia.it>
+
+AC_INIT(msqlmod.c)
+AC_CONFIG_HEADER(msql_config.h)
+
+AC_ARG_WITH(msql,    [  --with(out)-msql      mSQL Database engine support ],[]
+,[with_msql=yes])
+
+echo "Configuring msql module, (C) 1996,1997 Francesco Chemolli <kinkie@comedia.it>"
+
+sinclude(../module_configure.in)
+
+dnl set up "reasonable" search paths
+pike_msql_reasonable_prefixes="/usr/local /usr /opt"
+pike_msql_reasonable_directories="Minerva Hughes minerva hughes msql Msql msql2 Msql2 /"
+pike_msql_extra_include_directories="$HOME/include"
+pike_msql_extra_lib_directories="$HOME/lib"
+
+for a in $pike_msql_reasonable_prefixes
+do
+	for b in $pike_msql_reasonable_directories
+	do
+		pike_msql_reasonable_include_searchpath="$pike_msql_reasonable_include_searchpath $a/$b/include"
+		pike_msql_reasonable_lib_searchpath="$pike_msql_reasonable_lib_searchpath $a/$b/lib"
+	done
+done
+pike_msql_reasonable_include_searchpath="$pike_msql_reasonable_include_searchpath $pike_msql_extra_include_directories"
+pike_msql_reasonable_lib_searchpath="$pike_msql_reasonable_lib_searchpath $pike_msql_extra_lib_directories"
+
+dnl let's start the actual tests
+if test x$with_msql = xyes; then
+	OLD_CPPFLAGS=$CPPFLAGS
+	OLD_LIBS=$LIBS
+	OLD_LDFLAGS=$LDFLAGS
+
+	AC_MSG_CHECKING(for location of mSQL include files)
+	AC_CACHE_VAL(pike_cv_msql_include_dir, [
+		for pike_cv_msql_include_dir in $pike_msql_reasonable_include_searchpath no
+		do
+			if test -f $pike_cv_msql_include_dir/msql.h; then
+				break
+			fi
+		done ])
+	if test x$pike_cv_msql_include_dir != xno; then
+		AC_MSG_RESULT(found.)
+	else
+		AC_MSG_RESULT(not found.)
+	fi
+dnl	AC_MSG_RESULT($pike_cv_msql_include_dir)
+
+	if test x$pike_cv_msql_include_dir != xno; then
+		CPPFLAGS="$CPPFLAGS -I$pike_cv_msql_include_dir"
+	fi
+
+	AC_CHECK_HEADERS(msql.h)
+	
+	if test "$ac_cv_header_msql_h" != "no"; then
+		AC_MSG_CHECKING(for location of mSQL library)
+		AC_CACHE_VAL(pike_cv_msql_lib_dir, [ 
+			for pike_cv_msql_lib_dir in $pike_msql_reasonable_lib_searchpath no
+			do
+				if test -f $pike_cv_msql_lib_dir/libmsql.a; then
+					break
+				fi
+			done
+		] )
+		if test x$pike_cv_msql_lib_dir != xno; then
+			AC_MSG_RESULT(found.)
+		else
+			AC_MSG_RESULT(not found.)
+		fi
+dnl		AC_MSG_RESULT($pike_cv_msql_lib_dir)
+	fi
+
+	if test "$pike_cv_msql_lib_dir" != "no" ; then
+		LDFLAGS="$LDFLAGS -L$pike_cv_msql_lib_dir"
+		# On some OS's (Solaris 2) these are requiered
+		AC_CHECK_LIB(socket, socket, [], [])
+		AC_CHECK_LIB(nsl, gethostbyname, [], [])
+
+		AC_CHECK_LIB(msql, msqlClose)
+		if test "$ac_cv_lib_msql_msqlClose" != "no"; then
+			pike_cv_found_msql="yes"
+			MSQL_SUPPORTED="yes"
+		fi
+	fi
+
+	if test x$pike_cv_found_msql = xyes; then
+		AC_CHECK_FUNCS(msqlListIndex)
+		if test "$ac_cv_func_msqlListIndex" = "yes"; then
+			echo "We're supporting mSQL version 2.0, good..."
+		else
+			echo "We're supporting mSQL version 1.0"
+		fi
+	fi
+fi
+
+if test "x$pike_cv_found_msql" != "xyes"; then
+	CPPFLAGS=$OLD_CPPFLAGS
+	LDFLAGS=$OLD_LDFLAGS
+	LIBS=$OLD_LIBS
+fi
+
+AC_SUBST(LIBS)
+AC_SUBST(MSQL_SUPPORTED)
+
+AC_OUTPUT(./Makefile)
diff --git a/src/modules/Msql/doc/msql b/src/modules/Msql/doc/msql
new file mode 100644
index 0000000000..bb7d9182bf
--- /dev/null
+++ b/src/modules/Msql/doc/msql
@@ -0,0 +1,371 @@
+NAME
+	/precompiled/sql/msql - mSQL SQL server interface
+
+AUTHOR
+	Francesco Chemolli <kinkie@comedia.it>
+
+DESCRIPTION
+	This is an interface to the mSQL database server.
+	This module may or may not be availible on your Pike, depending
+	whether the appropriate include and library files (msql.h and libmsql.a
+	respectively) could be found at compile-time. Note that you DO NOT
+	need to have a mSQL server running on your host to use this module:
+	you can connect to the database over a TCP/IP socket
+
+	Please notice that unless you wish to specifically connect to a mSQL
+	server, you'd better use the /precompiled/sql program instead.
+	I and Henrik Grubbstrom have (hopefully) designed a server-independent
+	sql-server-class. The interfaces to all existing sql-classes
+	are consistant. Using /precompiled/sql ensures that your pike
+	applications will run with any supported SQL server without changing
+	a single line of code.
+
+	Also notice that we are currently undergoing a major release change
+	for mSQL. Some functions may mSQL/2.0 -specific, and thus missing on
+	hosts runnign mSQL/1.0.*
+
+SEE ALSO
+	/precompiled/sql/mysql, /precompiled/sql
+
+============================================================================
+NAME
+	create - initialize module and/or select database
+
+SYNTAX
+	#include <msql.h>
+	void msql->create();
+	or
+	void msql->create (string dbserver);
+	or
+	void msql->create (string dbserver, string dbname);
+
+DESCRIPTION
+	With one argument, this function
+	tries to connect to the specified (use hostname or IP address) database
+	server. To connect to a server running on the local host via UNIX domain
+	sockets use "localhost". To connect to the local host via TCP/IP sockets
+	you have to use the IP address "127.0.0.1".
+	With two arguments it also selects a database to use on the server.
+	With no arguments it tries to connect to the server on localhost, using
+	UNIX sockets.
+
+NOTA BENE
+	You need to have a database selected before using it, otherwise you'll
+	get exceptions when you try to query it.
+	Also notice that this function CAN raise exceptions if the db
+	server doesn't respond, if the database doesn't exist or is not accessible
+	by you.
+	You don't need bothering about syncronizing the connection to the database:
+	it is automatically closed (and the database is sync-ed) when the msql
+	object is destroyed.
+
+SEE ALSO
+	msql->select_db
+
+============================================================================
+NAME
+	select_db - Select the database you're using
+
+SYNTAX
+	#include <msql.h>
+	void msql->select_db(string dbname);
+
+DESCRIPTION
+	Before querying a database you have to select it. This can be accomplished
+	in two ways: the first is calling the create() function with two arguments,
+	another is calling it with only one argument and then calling select_db().
+	You can also use this function to change the database you're querying,
+	as long as it is on the same server you connected earlier.
+
+NOTA BENE
+	This function CAN raise exceptions in case something goes wrong
+	(for example: unexistant database, insufficient permissions, whatever).
+
+SEE ALSO
+	msql->create
+
+============================================================================
+NAME
+	query - query the db server
+
+SYNTAX
+	#include <msql.h>
+	array(mapping(string:mixed)) query (string sql_query);
+
+DESCRIPTION
+	This is all you need to query the database. It takes as argument an SQL
+	query string (i.e.: "SELECT foo,bar FROM baz WHERE name like '%kinkie%'" 
+	or "INSERT INTO baz VALUES ('kinkie','made','this')")
+	and returns a data structure containing the returned values.
+	The structure is an array (one entry for each returned row) of mappings
+	which have the column name as index and the column contents as data.
+	So to access a result from the first example you would have to do
+	"results[0]->foo"
+	A query which returns no data results in an empty array (and NOT in a 0).
+	Also notice that when there is a column name clash (that is: when you
+	join two or more tables which have columns with the same name), the
+	clashing columns are renamed to <tablename>+"."+<column name>.
+	To access those you'll have to use the indexing operator '[]
+	(i.e.: results[0]["foo.bar"]).
+	Errors (both from the interface and the SQL server) are reported via
+	exceptions, you may definitely want to catch them.
+	Also note that if the query is NOT a of SELECT type, but UPDATE or
+	MODIFY, the returned value is an empty array.
+	THIS IS NOT AN ERROR. Errors are reported ONLY via exceptions.
+	Error messages are not particularly verbose, since they account
+	only for errors inside the driver. To get server-related error messages,
+	you have to use the msql->error() function.
+
+SEE ALSO
+	msql->error
+
+============================================================================
+NAME
+	list_dbs - list all databases availible on the server
+
+SYNTAX
+	#include <msql.h>
+	array(string) list_dbs();
+	or
+	array(string) list_dbs(string wildcard);
+
+DESCRIPTION
+	Returns an array containing the names of all databases availible on
+	the system. Will throw an exception if there is no server connected.
+	If an argument is specified, it will return only those databases
+	whose name matches the given wildcard (which must be a glob).
+
+============================================================================
+NAME
+	list_tables - list all the tables in the current database
+
+SYNTAX
+	#include <msql.h>
+	array(string) list_tables();
+	or
+	array(string) list_tables(string wildcard);
+
+DESCRIPTION
+	Returns an array containing the names of all the tables in the currently
+	selected database. Will throw an exception if we aren't connected to
+	a database.
+	If an argument is specified, it will return only those tables
+	whose name matches the given wildcard (which must be a glob).
+
+============================================================================
+NAME
+	list_fields - describe the fields of a table in the database.
+
+SYNTAX
+	mapping(string:mapping(string:mixed)) list_fields(string table);
+
+DESCRIPTION
+	Returns a mapping describing the fields of a table in the database.
+	The returned value is a mapping, indexed on the column name,
+	of mappings.
+	These contain curretly the fields:
+
+		"type"(string) describes the field's mSQL data type ("char","integer",...)
+
+		"length"(int) it describes the field's length. It is only interesting for
+		char fields, in fact. Notice that mSQL/2.0's "text" fields
+		make this type obsolete, and this information useless. Also
+		notice that the strings returned by msql->query() have the correct length.
+		This field only specifies the _maximum_ length a "char()" field can have.
+
+		"table"(string) The table this field is in. Added only for
+		interface compliancy.
+
+		"flags"(multiset(string)) it's a multiset containing textual
+		descriptions of the server's flags associated with the current field.
+		Currently it can be empty, or contain "unique" or "not null".
+
+
+
+WARNING
+	As of version 1.0 of the mSQL module and 1.4 of the mysql module, this
+	function is _NOT_ generic-sql-interface compliant (it misses the
+	second argument, and this will break the interface).
+
+SEE ALSO
+	msql->query
+
+============================================================================
+NAME
+	error - tell what the last server error was.
+
+SYNTAX
+	string error();
+
+DESCRIPTION
+	This function returns the textual description of the last server-related
+	error. Returns 0 if no error has occurred yet.
+
+SEE ALSO
+	msql->query
+
+============================================================================
+NAME
+	server_info - describe what server are we talking to.
+
+SYNTAX
+	string server_info();
+
+DESCRIPTION
+	This function returns a string describing the server we are talking
+	to. It has the form "servername/serverversion" (like the HTTP protocol
+	description) and is most useful in conjunction with the generic SQL-server
+	module.
+
+SEE ALSO
+	/precompiled/sql
+
+============================================================================
+NAME
+	host_info - describe the connection we're using
+
+SYNTAX
+	string host_info();
+
+DESCRIPTION
+	This function returns a string describing what host are we talking to,
+	and how (TCP/IP or UNIX sockets).
+
+============================================================================
+NAME
+	create_db - create a new database
+
+SYNTAX
+	void create_db(string dbname);
+
+DESCRIPTION
+	This function creates a new database with the given name (assuming we
+	have enough permissions to do this).
+
+============================================================================
+NAME
+	drop_db - destroy a database
+
+SYNTAX
+	void drop_db(string dbname);
+
+DESCRIPTION
+	This function destroys a database and all the data it contains (assuming
+	we have enough permissions to do so). USE WITH CAUTION!
+
+============================================================================
+NAME
+	shutdown - shut down a server
+
+SYNTAX
+	void shutdown();
+
+DESCRIPTION
+	This function shuts a SQL-server down.
+
+============================================================================
+NAME
+	reload_acl - reloads a server's ACL
+
+SYNTAX
+	void reload_acl();
+
+DESCRIPTION
+	This function forces a server to reload its ACLs.
+
+============================================================================
+NAME
+	affected_rows - show how many rows were 'touched' by last operation
+
+SYNTAX
+	int affected_rows();
+
+DESCRIPTION
+	This function returns how many rows in the database were affected by
+	our last SQL query.
+
+NOTA BENE
+	This function is availible only if we're supporting mSQL server version 2
+	or later. (That means: if the includes and library of version 2 of mSQL
+	were availible when the module was compiled).
+
+============================================================================
+NAME
+	list_index - show the index structure for a table
+
+SYNTAX
+	array(string) list_index(string tablename, string indexname);
+
+DESCRIPTION
+	This function returns an array describing the index structure for the
+	given table and index name, as defined by the non-standard SQL query
+	'create index' (see the mSQL documentation for further informations).
+	More than one index can be created for a table. There's currently NO way
+	to have a listing of the indexes defined for a table (blame it on
+	the mSQL API).
+
+NOTA BENE
+	This function is availible if support the module is for mSQL version 2
+	or later.
+
+============================================================================
+EXTERNAL INFLUENCES on the module.
+	These ones do not depend on my implementation really, they're just part
+	of the mSQL C API. They take the form of certain environment variables
+	which, if defined in the environment of the pike interpreter, influence
+	the interface's behavior. Those are "MSQL_TCP_PORT" which forces the
+	server to connect to a port other than the default, "MSQL_UNIX_PORT", same
+	as above, only referring to the UNIX domain sockets. If you built your
+	mSQL server with the default setttings, you shouldn't worry about these.
+	The variable MINERVA_DEBUG can be used to debug the mSQL API (you
+	shouldn't worry about this either). Refer to the mSQL documentation
+	for further details.
+
+NOTES, TODOs AND OTHER THINGS
+	This program is my first attempt at touching the Pike source code, so
+	I may well have messed everything up. I hope I didn't.
+
+	Note that although the database engine should theoretically
+	store any kind of values, the interface seems to be quite text-oriented:
+	strings are returned as null-terminated char*, and not as a
+	(datum,len) couple. For now I followed this trend. I will change this
+	behavior as soon as I can think of a safe way.
+
+	I didn't make any testsuite for this module, since to test anything it would
+	require a working mSQL server, and I can't make any assumption
+	on those... You can try to use the included "test_msql.pike" script,
+	but you'll possibly have to patch it.
+
+	Also note that THIS MODULE USES BLOCKING I/O to connect to the server.
+	mSQL should be reasonably fast, but you might want to consider this
+	particular aspect. It should however be thread-safe.
+
+THANKS
+	Many thanks to Henrik Grubbström, without whose help this piece of
+	code couldn't be nearly as complete as it is.
+	Also thanks to Frederik Hubinette, for designing such a nice language
+	as Pike.
+
+COPYRIGHT
+	This code and documentation are copyright 1997 Francesco Chemolli 
+	<kinkie@comedia.it>.
+	It may be copied, modified, redistributed under the terms of the
+	GNU General Public License, version 2.
+	
+	This program is distributed in the hope that it will be useful,
+	but WITHOUT ANY WARRANTY; without even the implied warranty of
+	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+	GNU General Public License for more details.
+
+	You should have received a copy of the GNU General Public License
+	along with the Pike source. If not, write to the Free Software
+	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+	The mSQL (Mini-SQL) server and its accompanying tools and documentation are 
+	copyright 1993-1996 Hughes Technologies Pty Ltd, and are free to use
+	for non-commercial entities. For informations about Mini SQL please
+	consult the Hughes Thechnologies WWW site, at http://hughes.com.au/
+
+	The Pike programming language is copyright Frederik Hubinette, and is
+	distributed under the terms of the GNU General Public License. For furhter
+	information, please consult the Pike WWW site, at http://pike.infovav.se/
diff --git a/src/modules/Msql/msql_config.h.in b/src/modules/Msql/msql_config.h.in
new file mode 100644
index 0000000000..bb1bf04953
--- /dev/null
+++ b/src/modules/Msql/msql_config.h.in
@@ -0,0 +1,23 @@
+/* $Id: msql_config.h.in,v 1.1.1.1 1997/10/14 21:55:42 grubba Exp $ */
+#undef STDC_HEADERS
+
+#undef HAVE_MSQL_H
+
+#undef HAVE_LIBMSQL
+
+#undef HAVE_MSQLLISTINDEX
+
+#undef HAVE_PIKE_SVALUE_H
+
+/* End of autoconfigurable section */
+#undef HAVE_MSQL
+
+#ifdef HAVE_MSQL_H
+#ifdef HAVE_LIBMSQL
+#define HAVE_MSQL
+#endif
+#endif
+
+#ifdef HAVE_MSQLLISTINDEX
+#define MSQL_VERSION_2
+#endif
diff --git a/src/modules/Msql/msqlmod.c b/src/modules/Msql/msqlmod.c
new file mode 100644
index 0000000000..48d48915ac
--- /dev/null
+++ b/src/modules/Msql/msqlmod.c
@@ -0,0 +1,783 @@
+/*
+ * This code is (C) Francesco Chemolli, 1997.
+ * You may use, modify and redistribute it freely under the terms
+ * of the GNU General Public License, version 2.
+ * $Id: msqlmod.c,v 1.1.1.1 1997/10/14 21:55:42 grubba Exp $
+ *
+ * This versione is intended for Pike/0.5 and later.
+ * It won't compile under older versions of the Pike interpreter.
+ */
+
+/* All this code is pretty useless if we don't have a msql library...*/
+#include "msql_config.h"
+#ifdef HAVE_MSQL
+
+/* #define MSQL_DEBUG 1 */
+
+#ifdef MSQL_DEBUG
+#include <stdio.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+
+#include <machine.h>
+#include <svalue.h>
+#include <threads.h>
+#include <global.h>
+#include <interpret.h>
+#include <program.h>
+#include <array.h>
+#include <mapping.h>
+#include <stralloc.h>
+#include <builtin_functions.h>
+#include <module_support.h>
+
+RCSID("$Id: msqlmod.c,v 1.1.1.1 1997/10/14 21:55:42 grubba Exp $");
+
+#ifdef _REENTRANT
+MUTEX_T pike_msql_mutex;
+#define MSQL_LOCK() mt_lock(&pike_msql_mutex)
+#define MSQL_UNLOCK() mt_unlock(&pike_msql_mutex)
+#else
+#define MSQL_LOCK() /**/
+#define MSQL_UNLOCK() /**/
+#endif
+
+#include <msql.h>
+
+static char * decode_msql_type (int msql_type)
+{
+	switch (msql_type) {
+		case INT_TYPE: return "int";
+		case CHAR_TYPE: return "char";
+		case REAL_TYPE: return "real";
+		case IDENT_TYPE: return "ident";
+		case NULL_TYPE: return "null";
+#ifdef MSQL_VERSION_2
+		case TEXT_TYPE: return "text";
+		case UINT_TYPE: return "unsigned int";
+		case IDX_TYPE: return "index";
+		case SYSVAR_TYPE: return "sysvar";
+		case ANY_TYPE: return "any";
+#endif
+		default: return "unknown";
+	}
+};
+
+struct msql_my_data
+{
+	int socket; /* the communication socket between us and the database engine. */
+	int db_selected:1; /*flag: if we selected a database*/
+	int connected:1; /*flag: we connected to a server*/
+	struct pike_string *error_msg;
+#ifdef MSQL_VERSION_2
+	int affected;
+#endif
+};
+
+#define THIS ((struct msql_my_data *) fp->current_storage)
+
+static void msql_object_created (struct object *o)
+{
+	THIS->connected=0;
+	THIS->db_selected=0;
+	THIS->error_msg=NULL;
+}
+
+static void msql_object_destroyed (struct object * o)
+{
+	if (THIS->connected) {
+		int socket=THIS->socket;
+		THREADS_ALLOW();
+		MSQL_LOCK();
+		msqlClose(socket);
+		MSQL_UNLOCK();
+		THREADS_DISALLOW();
+	}
+	if (THIS->error_msg)
+		free_string(THIS->error_msg);
+}
+
+static void report_error (void)
+{
+	if (THIS->error_msg)
+		free_string(THIS->error_msg);
+	THIS->error_msg=make_shared_string(msqlErrMsg);
+	/* msqlErrMsg is really a char[160] in mSQL/2.0, but I don't want
+		 to take any chances, even if I'm wasting some time here. */
+}
+
+static void do_select_db(char * dbname)
+{
+	/* NOTICE: We're assuming we're connected. CHECK before calling! */
+	int status,socket=THIS->socket;
+
+	THREADS_ALLOW();
+	MSQL_LOCK();
+	status=msqlSelectDB(socket,dbname);
+	MSQL_UNLOCK();
+	THREADS_DISALLOW();
+
+	if (status==-1)
+	{
+		THIS->db_selected=0;
+		report_error();
+		error("Could not select database.\n");
+	}
+	THIS->db_selected=1;
+	return;
+}
+
+/* void shutdown() */
+static void do_shutdown (INT32 args)
+/* Notice: the msqlShutdown() function is undocumented. I'll have to go
+	 through the source to find how to report errors.*/
+{
+	int status=0,socket=THIS->socket;
+	check_all_args("Msql->shutdown",args,0);
+	pop_n_elems(args);
+
+	if (!THIS->connected)
+		error ("Not connected to any server.\n");
+
+	THREADS_ALLOW();
+	MSQL_LOCK();
+	status=msqlShutdown(socket);
+	if (status>=0)
+		msqlClose(socket); /*DBserver is shut down, might as well close */
+	MSQL_UNLOCK();
+	THREADS_DISALLOW();
+	if (status<0) {
+		report_error();
+		error ("Error while shutting down the DBserver, connection not closed.\n");
+	}
+	THIS->connected=0;
+	THIS->db_selected=0;
+}
+
+/* void reload_acl() */
+static void do_reload_acl (INT32 args)
+/* Undocumented mSQL function. */
+{
+	int socket,status=0;
+	check_all_args("Msql->reload_acl",args,0);
+	pop_n_elems(args);
+	if (!THIS->connected)
+		error ("Not connected to any server.\n");
+
+	socket=THIS->socket;
+	THREADS_ALLOW();
+	MSQL_LOCK();
+	status=msqlReloadAcls(socket);
+	MSQL_UNLOCK();
+	THREADS_DISALLOW();
+	if (status<0) {
+		report_error();
+		error ("Could not reload ACLs.\n");
+	}
+}
+
+/* void create (void|string dbserver, void|string dbname,
+	void|string username, void|string passwd) */
+static void msql_mod_create (INT32 args)
+{
+	struct pike_string * arg1=NULL, *arg2=NULL;
+	int sock, status;
+
+	check_all_args("Msql->create",args,
+			BIT_STRING|BIT_VOID,BIT_STRING|BIT_VOID,BIT_STRING|BIT_VOID,
+			BIT_STRING|BIT_VOID,0);
+
+  if (args)
+    if (sp[-args].u.string->len)
+      arg1=sp[-args].u.string;
+  if (args >1)
+    if (sp[1-args].u.string->len)
+      arg2=sp[1-args].u.string;
+
+	/*Okay. We had one or two arguments, and we must connect to a server
+	and if needed select a database.
+	First off let's check whether we are already connected. In this case,
+	disconnect.*/
+	if (THIS->connected)
+	{
+		msqlClose (THIS->socket);
+		THIS->connected=0;
+		THIS->db_selected=0;
+	}
+
+	THREADS_ALLOW();
+	MSQL_LOCK();
+	/* Warning! If there were no args, we're deferencing a NULL pointer!*/
+	if (!arg1 || !strcmp (arg1->str,"localhost"))
+		sock=msqlConnect(NULL);
+	else
+		sock=msqlConnect(arg1->str);
+	MSQL_UNLOCK();
+	THREADS_DISALLOW();
+
+	if (sock==-1) {
+		THIS->db_selected=0;
+		THIS->connected=0;
+		report_error();
+		error("Error while connecting to mSQL server.\n");
+	}
+	THIS->socket=sock;
+	THIS->connected=1;
+	if (!arg2)
+		return;
+	do_select_db(arg2->str);
+	pop_n_elems(args);
+}
+
+/* array list_dbs(void|string wild) */
+static void do_list_dbs (INT32 args)
+{
+	m_result * result;
+	m_row row;
+	int fields,numrows=0,socket=THIS->socket;
+
+	check_all_args("Msql->list_dbs",args,BIT_STRING|BIT_VOID,0);
+
+	if (!THIS->connected)
+		error ("Not connected.\n");
+	if (args>0 && sp[-args].u.string->len)
+		/* We have a glob. We should pop the arg and push it again for a later
+		 * call to glob() */
+		;
+	else {
+		pop_n_elems(args);
+		args=0; /*Let's use args as a flag. If args==0, no globbing.*/
+	}
+
+	THREADS_ALLOW();
+	MSQL_LOCK();
+	result=msqlListDBs(socket);
+	MSQL_UNLOCK();
+	THREADS_DISALLOW();
+
+	if (!result) {
+		f_aggregate(0); /*empty array if no databases*/
+		return;
+	}
+	while (row=msqlFetchRow(result)) /*it's fast, we're in RAM*/
+	{
+		numrows++;
+		push_text(row[0]);
+	}
+	f_aggregate(numrows);
+	msqlFreeResult(result);
+	if (args)
+		f_glob(2);
+	return;
+}
+
+/* array list_tables(void|string wild) */
+static void do_list_tables (INT32 args)
+	/* ARGH! There's much code duplication here... but the subtle differences
+	 * between the various functions make it impervious to try to generalize..*/
+{
+	m_result * result;
+	m_row row;
+	int fields,numrows=0,socket=THIS->socket;
+
+	check_all_args ("Msql->list_tables",args,BIT_STRING|BIT_VOID,0);
+
+	if (!THIS->db_selected)
+		error ("No database selected.\n");
+
+	if (args>0 && sp[-args].u.string->len)
+		/* We have a glob. We should pop the arg and push it again for a later
+		 * call to glob() */
+		;
+	else {
+		pop_n_elems(args);
+		args=0; /*Let's use args as a flag. If args==0, no globbing.*/
+	}
+
+	THREADS_ALLOW();
+	MSQL_LOCK();
+	result=msqlListTables(socket);
+	MSQL_UNLOCK();
+	THREADS_DISALLOW();
+
+	if (!result) {
+		f_aggregate(0); /*empty array if no databases*/
+		return;
+	}
+	while (row=msqlFetchRow(result))
+	{
+		numrows++;
+		push_text(row[0]);
+	}
+	f_aggregate(numrows);
+	msqlFreeResult(result);
+	if (args)
+		f_glob(2);
+	return;
+}
+
+/* void select_db(string dbname) */
+static void select_db(INT32 args)
+{
+	struct pike_string * arg;
+	int status;
+
+	check_all_args("Msql->select_db",args,BIT_STRING,0);
+	if (!THIS->connected)
+		error ("Not connected.\n");
+	arg=sp[-args].u.string;
+	do_select_db(arg->str);
+	pop_n_elems(args);
+}
+
+/* array(mapping(string:mixed)) query (string sqlquery) */
+static void do_query (INT32 args)
+{
+	int status, num_fields,num_rows,j,k,tmp_socket,*types,*duplicate_names_map;
+	m_result * result;
+	m_field * current_field;
+	m_row row;
+	struct array *field_names, *retval;
+	char * query, ** names;
+
+	check_all_args("Msql->query",args,BIT_STRING,0);
+
+	if (!THIS->connected)
+		error("Must connect to database server before querying it.\n");
+	if (!THIS->db_selected)
+		error("Must select database before querying it.\n");
+
+	tmp_socket=THIS->socket;
+	query=sp[-args].u.string->str;
+
+#ifdef MSQL_DEBUG
+	printf ("MSQLMod: Query is\"%s\"\n",query);
+#endif
+
+	THREADS_ALLOW();
+	MSQL_LOCK();
+	status=msqlQuery(tmp_socket,query);
+	MSQL_UNLOCK();
+	THREADS_DISALLOW();
+
+	if (status==-1) {
+		report_error();
+		error("Error in SQL query.\n");
+	}
+
+#ifdef MSQL_VERSION_2
+	THIS->affected=status;
+#endif
+	pop_n_elems(args);
+	/*We have what we want. We need to construct the returned structure*/
+	THREADS_ALLOW();
+	MSQL_LOCK();
+	result=msqlStoreResult();
+	MSQL_UNLOCK();
+	THREADS_DISALLOW();
+	if (!result || !(num_rows=msqlNumRows(result)) ) {
+		f_aggregate(0);
+		return;
+	}
+
+	/*First off: we need to know the field names and types 
+	 *in their correct order, and store them in arrays for further analysis
+	 */
+	num_fields=msqlNumFields(result);
+	types = malloc (sizeof(int)*num_fields);
+	duplicate_names_map = malloc (sizeof(int)*num_fields);
+	names = malloc (sizeof(char*)*num_fields);
+	if (!types || !duplicate_names_map || !names)
+		error ("Memory exhausted.\n");
+
+	/* initialize support structure for duplicate column names */
+	for (j=0;j<num_fields;j++)
+		duplicate_names_map[j]=0;
+
+	/* Find duplicate column names */
+	for (j=0;j<num_fields;j++) {
+		current_field=msqlFetchField(result);
+		names[j]=current_field->name;
+	}
+	for (j=0;j<num_fields;j++)
+		for (k=j+1;k<num_fields;k++)
+			if (!strcmp(names[j],names[k])) {
+				duplicate_names_map[j]=1;
+				duplicate_names_map[k]=1;
+			}
+
+	/* Reset field cursor */
+	msqlFieldSeek(result,0);
+
+	/* create array containing column names */
+	for (j=0;j<num_fields;j++) {
+		current_field=msqlFetchField(result);
+		if (!current_field)
+			error ("Huh? Weird! Null field returned at index %d.\n",j);
+		if (duplicate_names_map[j]) { 
+			/* If we have a clashing column name we need to prepend "tablename."*/
+			push_text (current_field->table);
+			push_text (".");
+			push_text(current_field->name);
+			f_add(3);
+		}
+		else
+			push_text(current_field->name);
+		types[j]=current_field->type;
+	}
+
+	field_names=aggregate_array(num_fields);
+
+	/* Let's fetch the rows and accumulate them as mappings on the stack. */
+	for (j=0;j<num_rows;j++)
+	{
+		struct array * this_row;
+		struct mapping * this_result;
+		int k;
+		row=msqlFetchRow(result);
+		for (k=0;k<num_fields;k++) {
+			if (!row[k]) {
+				push_int(0);
+				continue;
+			}
+			switch (types[k]) {
+#ifdef MSQL_VERSION_2
+				case UINT_TYPE: /*this may lose the MSB and overflow.
+													Is htere a better way?*/
+#endif
+				case INT_TYPE: push_int(atoi(row[k])); break;
+				case REAL_TYPE: push_float(atof(row[k])); break;
+				default: push_text(row[k]); break;
+			}
+		}
+		this_row=aggregate_array(num_fields);
+		this_result=mkmapping(field_names,this_row);
+		push_mapping(this_result); /*we're putting mappings on the stack*/
+		free_array(this_row);
+	}
+	
+	/* Wrap it up and free the used memory */
+	f_aggregate(num_rows); /* aggregate and push the resulting array */
+	free_array(field_names);
+	free(types);
+	free(duplicate_names_map);
+	free(names);
+	msqlFreeResult(result);
+}
+
+/* string server_info() */
+static void do_info (INT32 args)
+{
+	char * info;
+
+	check_all_args("Msql->info",args,0);
+	pop_n_elems(args);
+	if (!THIS->connected)
+		error ("Not connected.\n");
+	push_text("msql/");
+	THREADS_ALLOW();
+	MSQL_LOCK();
+	info=msqlGetServerInfo();
+	MSQL_UNLOCK();
+	THREADS_DISALLOW();
+	push_text(info);
+	f_add(2);
+	return;
+}
+
+/* string host_info() */
+static void do_host_info (INT32 args)
+{
+	check_all_args("Msql->host_info",args,0);
+	pop_n_elems(args);
+	if (!THIS->connected)
+		error ("Not connected.\n");
+	/*it's local to the client library. Not even worth allowing
+	 context switches*/
+	push_text(msqlGetHostInfo()); 
+	return;
+}
+
+/* string error() */
+static void do_error (INT32 args)
+{
+	check_all_args("Msql->error",args,0);
+	pop_n_elems(args);
+	if (THIS->error_msg)
+		push_string(THIS->error_msg);
+	return;
+}
+
+/* void create_db (string dbname) */
+static void do_create_db (INT32 args)
+{
+	int dbresult;
+	char * dbname;
+	int socket;
+
+	check_all_args("Msql->create_db",args,BIT_STRING,0);
+
+	if (!THIS->connected)
+		error("Not connected.\n");
+	dbname = sp[-args].u.string->str;
+	socket=THIS->socket;
+	THREADS_ALLOW();
+	MSQL_LOCK();
+	dbresult=msqlCreateDB(socket,dbname);
+	MSQL_UNLOCK();
+	THREADS_DISALLOW();
+	if (dbresult==-1) {
+		report_error();
+		error ("Could not create database.\n");
+	}
+	pop_n_elems(args);
+}
+
+/* void drop_db (string dbname) */
+static void do_drop_db (INT32 args)
+{
+	int dbresult;
+	char * dbname;
+	int socket;
+	
+	check_all_args("Msql->drop_db",args,BIT_STRING,0);
+
+	if (!THIS->connected)
+		error("Not connected.\n");
+	dbname = sp[-args].u.string->str;
+	socket=THIS->socket;
+	THREADS_ALLOW();
+	MSQL_LOCK();
+	dbresult=msqlDropDB(socket,dbname);
+	MSQL_UNLOCK();
+	THREADS_DISALLOW();
+	if (dbresult==-1) {
+		report_error();
+		error ("Could not drop database.\n");
+	}
+	pop_n_elems(args);
+	return;
+}
+
+/* mapping(string:array(string)) list_fields (string table) */
+static void do_list_fields (INT32 args)
+{
+	m_result * result;
+	m_field * field;
+	char * table;
+	int fields, j, socket=THIS->socket;
+
+	check_all_args("Msql->list_fields",args,BIT_STRING,0);
+	if (!THIS->connected)
+		error ("Not connected.\n");
+	if (!THIS->db_selected)
+		error ("Must select a db first.\n");
+	table=sp[-args].u.string->str;
+#ifdef MSQL_DEBUG
+	printf ("list_fields: table=%s(%d)\n",sp[-args].u.string->str,sp[-args].u.string->len);
+#endif
+
+	THREADS_ALLOW();
+	MSQL_LOCK();
+	result=msqlListFields(socket,table);
+	MSQL_UNLOCK();
+	THREADS_DISALLOW();
+	pop_n_elems(args);
+
+	if (!result) {
+		report_error();
+		error ("No fields information.\n");
+	}
+	
+	fields = msqlNumFields(result);
+	if (!fields)
+		error ("No such table.\n");
+
+	for (j=0;j<fields;j++)
+	{
+		int flagsnum=0;
+		field=msqlFetchField(result);
+		push_text(field->name);
+	
+		push_text("type");
+		push_text(decode_msql_type(field->type));
+		push_text("length");
+		push_int(field->length);
+		push_text("table");
+		push_text(field->table);
+		push_text("flags");
+#ifdef IS_UNIQUE
+		if (IS_UNIQUE(field->flags)) {
+			push_text("unique");
+			flagsnum++;
+		}
+#endif
+#ifdef IS_NOT_NULL
+		if (IS_NOT_NULL(field->flags)) {
+			push_text("not_null");
+			flagsnum++;
+		}
+#endif
+#ifdef IS_PRI_KEY
+		if (IS_PRI_KEY(field->flags)) {
+			push_text("primary_key");
+			flagsnum++;
+		}
+#endif
+		f_aggregate_multiset(flagsnum);
+		f_aggregate_mapping(8); /*must aggregate on the fields above, except name*/
+	}
+	f_aggregate_mapping(fields*2);
+
+	msqlFreeResult(result);
+	return;
+}
+
+#ifdef MSQL_VERSION_2
+/* int affected_rows() */
+static void do_affected_rows (INT32 args)
+{
+	check_all_args("Msql->affected_rows",args,0);
+	pop_n_elems(args);
+	push_int(THIS->affected);
+	return;
+}
+
+/* array list_index(string tablename, string indexname) */
+static void do_list_index (INT32 args)
+{
+	char * arg1, *arg2;
+	m_result * result;
+	m_row row;
+	int sock, rows, j;
+
+	check_all_args("Msql->list_index",args,BIT_STRING,BIT_STRING,0);
+	if (!THIS->db_selected)
+		error ("No database selected.\n");
+	arg1=sp[-args].u.string->str;
+	arg2=sp[1-args].u.string->str;
+	sock=THIS->socket;
+	THREADS_ALLOW();
+	MSQL_LOCK();
+	result=msqlListIndex(sock,arg1,arg2);
+	MSQL_UNLOCK();
+	THREADS_DISALLOW();
+	pop_n_elems(args);
+	if (!result || !(rows=msqlNumRows(result)) ) {
+		f_aggregate(0);
+		return;
+	}
+	msqlFetchRow(result); /*The first one is the internal type, useless*/
+	rows--;
+	for (j=0; j<rows; j++)
+	{
+		row=msqlFetchRow(result);
+		push_text(row[0]);
+	}
+	f_aggregate(rows);
+	return;
+}
+#endif
+
+void pike_module_init(void)
+{
+	start_new_program();
+	add_storage(sizeof(struct msql_my_data));
+
+	set_init_callback (msql_object_created);
+	set_exit_callback (msql_object_destroyed);
+
+	add_function("create",msql_mod_create,
+			"function(void|string,void|string,void|string,void|string:void)",
+			OPT_EXTERNAL_DEPEND);
+	/* 1st arg: hostname or "localhost", 2nd arg: dbname or nothing 
+	 * CAN raise exception if there is no server listening, or no database
+	 * To connect using the UNIX socket instead of a localfunction use the
+	 * hostname "localhost", or use no argument. It will use UNIX sockets.
+	 * Third and fourth argument are currently ignored, since mSQL doesn't
+	 * support user/passwd authorization. The user will be the owner of
+	 * the current process.
+	 */
+
+	add_function("select_db",select_db,"function(string:void)",
+		OPT_EXTERNAL_DEPEND);
+	/* if no db selected by connect, does it now.
+	 * CAN raise an exception if there's no such database or we haven't selected
+	 * an host.
+	 */
+
+	add_function("query",do_query,
+			"function(string:array(mapping(string:mixed)))",
+			OPT_ASSIGNMENT|OPT_TRY_OPTIMIZE|OPT_EXTERNAL_DEPEND|OPT_RETURN);
+	/* Gets an SQL query, and returns an array of the results, one element
+	 * for each result line, each row is a mapping with the column name as
+	 * index and the data as value.
+	 * CAN raise excaptions if there's no active database.
+	 */
+
+	add_function ("list_dbs",do_list_dbs,
+		"function(void|string:array(string))",
+		OPT_ASSIGNMENT|OPT_EXTERNAL_DEPEND|OPT_RETURN);
+	/* Lists the tables contained in the selected database. */
+
+	add_function ("list_tables",do_list_tables,
+		"function(void:array(string))",
+		OPT_ASSIGNMENT|OPT_EXTERNAL_DEPEND|OPT_RETURN);
+	/* Lists the tables contained in the selected database. */
+
+	add_function ("list_fields", do_list_fields,
+		"function(string:mapping(string:array(mixed)))",
+		OPT_RETURN|OPT_EXTERNAL_DEPEND);
+	/* Returns information on the the fields of the given table of the current
+	  database */
+
+	add_function ("error",do_error, "function(void:void|string)",
+		OPT_RETURN|OPT_EXTERNAL_DEPEND);
+	/* return the last error reported by the server. */
+
+	add_function ("server_info", do_info, "function(void:string)",
+		OPT_RETURN|OPT_EXTERNAL_DEPEND);
+	/* Returns "msql/<server_version>" */
+
+	add_function ("host_info", do_host_info, "function(void:string)",
+			OPT_EXTERNAL_DEPEND|OPT_RETURN);
+	/* Returns information on the connection type and such */
+
+	add_function ("create_db", do_create_db, "function(string:void)",
+		OPT_EXTERNAL_DEPEND);
+	/* creates a new database with the name as argument */
+
+	add_function ("drop_db", do_drop_db, "function(string:void)",
+		OPT_EXTERNAL_DEPEND);
+	/* destroys a database and its contents */
+
+	add_function ("shutdown", do_shutdown, "function(void:void)",
+			OPT_EXTERNAL_DEPEND);
+	/* Shuts the server down */
+
+	add_function ("reload_acl", do_reload_acl, "function(void:void)",
+			OPT_EXTERNAL_DEPEND);
+	/* Reloads the ACL for the DBserver */
+
+#ifdef MSQL_VERSION_2
+	add_function ("affected_rows", do_affected_rows, "function(void:int)",
+		OPT_RETURN|OPT_EXTERNAL_DEPEND);
+	/* Returns the number of rows 'touched' by last query */
+	/* UNTESTED */
+
+	add_function ("list_index", do_list_index, "function(string,string:array)",
+			OPT_EXTERNAL_DEPEND);
+	/* Returns the index structure on the specified table */
+	/* UNTESTED */
+#endif
+
+	end_class("msql",0);
+}
+
+#else /*HAVE_MSQL*/
+void pike_module_init(void) {}
+#endif /*HAVE_MSQL*/
+
+void pike_module_exit (void) { }
diff --git a/src/modules/Msql/test_msqlmod.pike b/src/modules/Msql/test_msqlmod.pike
new file mode 100755
index 0000000000..65ff5df3d2
--- /dev/null
+++ b/src/modules/Msql/test_msqlmod.pike
@@ -0,0 +1,33 @@
+#!../../pike
+#include <msql.h>
+
+int main()
+{
+	array result;
+	string table;
+	mixed err;
+
+	object db;
+	
+	db=msql();
+	werror (sprintf ("error :'%O'\n",db->error()));
+
+	result=db->list_dbs();
+	write(sprintf("Databases: %O\n",result));
+	write ("connecting to database "+result[0]+"\n");
+	db->select_db(result[0]);
+	result = db->list_tables();
+	write(sprintf("Tables: %O\n",result));
+	table=result[0];
+	write("*** Dumping fields of table: "+table+"\n");
+	catch {result=db->list_fields(table);};
+	write(sprintf("%O\n",result));
+	write ("*** Dumping contents of table:"+table+"\n");
+	write(sprintf("%O\n",db->query("","")));
+	err = catch (write(sprintf("%O\n",db->query("Select * from "+table))));
+	if (err)
+		write (db->error());
+	write(sprintf("%O\n",db->error()));
+	write(db->server_info()+"\n");
+	write(db->host_info()+"\n");
+}
diff --git a/src/modules/Msql/testsuite.in b/src/modules/Msql/testsuite.in
new file mode 100644
index 0000000000..e69de29bb2
-- 
GitLab