[libgda] MSAccess provider: added an embedded version of MDBTools' code for Windows build



commit 24937af76c4671dced49a3835ae2d9f1bf69f89a
Author: Vivien Malerba <malerba gnome-db org>
Date:   Tue Feb 22 21:18:44 2011 +0100

    MSAccess provider: added an embedded version of MDBTools' code for Windows build
    
    and added the --enable-system-mdbtools which enables one to
    use the embedded version on other builds (the default behaviour
    is unchanged from previous versions)

 configure.ac                                  |   13 +-
 installers/Windows/make-zip-setup.sh          |    4 -
 m4/mdbtools.m4                                |   19 +-
 providers/mdb/Makefile.am                     |   13 +-
 providers/mdb/libmdb-src/Makefile.am          |   27 +
 providers/mdb/libmdb-src/backend.c            |  308 ++++++++
 providers/mdb/libmdb-src/catalog.c            |  139 ++++
 providers/mdb/libmdb-src/data.c               |  945 +++++++++++++++++++++++++
 providers/mdb/libmdb-src/dump.c               |   39 +
 providers/mdb/libmdb-src/file.c               |  389 ++++++++++
 providers/mdb/libmdb-src/iconv.c              |  214 ++++++
 providers/mdb/libmdb-src/include/mdbodbc.h    |   70 ++
 providers/mdb/libmdb-src/include/mdbprivate.h |   35 +
 providers/mdb/libmdb-src/include/mdbsql.h     |   86 +++
 providers/mdb/libmdb-src/include/mdbtools.h   |  542 ++++++++++++++
 providers/mdb/libmdb-src/include/mdbver.h     |   26 +
 providers/mdb/libmdb-src/index.c              |  886 +++++++++++++++++++++++
 providers/mdb/libmdb-src/kkd.c                |  149 ++++
 providers/mdb/libmdb-src/like.c               |   78 ++
 providers/mdb/libmdb-src/map.c                |  133 ++++
 providers/mdb/libmdb-src/mem.c                |   52 ++
 providers/mdb/libmdb-src/money.c              |  138 ++++
 providers/mdb/libmdb-src/options.c            |   87 +++
 providers/mdb/libmdb-src/props.c              |  129 ++++
 providers/mdb/libmdb-src/sargs.c              |  266 +++++++
 providers/mdb/libmdb-src/stats.c              |   74 ++
 providers/mdb/libmdb-src/table.c              |  371 ++++++++++
 providers/mdb/libmdb-src/worktable.c          |   99 +++
 providers/mdb/libmdb-src/write.c              |  868 +++++++++++++++++++++++
 29 files changed, 6187 insertions(+), 12 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index 419df31..136d3af 100644
--- a/configure.ac
+++ b/configure.ac
@@ -574,7 +574,15 @@ fi
 AM_CONDITIONAL(FIREBIRD, test x$firebirddir != x)
 
 dnl Test for MDB Tools (for MS Access files)
-MDBTOOLS_CHECK([$lib])
+if test "x$platform_win32" = "xyes"; then
+    AM_ICONV
+    AM_CONDITIONAL(MDB, true)
+    AC_DEFINE(MDB_BIND_COLUMN_FOUR_ARGS,[1],[define if mdb_bind_column accepts four args])
+    AC_DEFINE(MDB_WITH_WRITE_SUPPORT,[1],[define if mdb_open accepts MDB_WRITABLE])
+    mdbtools_found="yes (embedded)"
+else
+    MDBTOOLS_CHECK([$lib])
+fi
 
 dnl test for system-installed SQLite
 dnl If no SQLite is found or if the SQLite found does not have the sqlite3_table_column_metadata()
@@ -582,7 +590,7 @@ dnl function call, then use the embedded version
 dnl also, on Win32, always use the embedded version
 if test x"$platform_win32" = "xyes"
 then
-        AM_CONDITIONAL(HAVE_SQLITE, 0)
+        AM_CONDITIONAL(HAVE_SQLITE, false)
 else
 	AC_ARG_ENABLE(system-sqlite,
 		AS_HELP_STRING([--enable-system-sqlite], [Use SQLite installed on the system [default=no]]),
@@ -802,6 +810,7 @@ providers/bdb/libgda-bdb-4.0.pc
 providers/bdbsql/Makefile
 providers/bdbsql/libgda-bdbsql-4.0.pc
 providers/mdb/Makefile
+providers/mdb/libmdb-src/Makefile
 providers/mdb/libgda-mdb-4.0.pc
 providers/mysql/Makefile
 providers/mysql/libgda-mysql-4.0.pc
diff --git a/installers/Windows/make-zip-setup.sh b/installers/Windows/make-zip-setup.sh
index 9b1d911..a1838cf 100755
--- a/installers/Windows/make-zip-setup.sh
+++ b/installers/Windows/make-zip-setup.sh
@@ -248,10 +248,6 @@ files=(libdb51.dll)
 add_files_to_zip $archive_ext ${depend_path}/bdb bin $files
 add_files_to_nsh prov_bdb ${depend_path}/bdb bin $files
 
-files=(libmdb-0.dll)
-add_files_to_zip $archive_ext ${depend_path}/mdb bin $files
-add_files_to_nsh prov_mdb ${depend_path}/mdb bin $files
-
 files=(libmySQL.dll)
 add_files_to_zip $archive_ext ${depend_path}/mysql bin $files
 add_files_to_nsh prov_mysql ${depend_path}/mysql bin $files
diff --git a/m4/mdbtools.m4 b/m4/mdbtools.m4
index 7931fc7..0fcaf1a 100644
--- a/m4/mdbtools.m4
+++ b/m4/mdbtools.m4
@@ -44,7 +44,7 @@ dnl   This macro always calls:
 dnl
 dnl    AC_SUBST(MDB_LIBS)
 dnl    AC_SUBST(MDB_CFLAGS)
-dnl    mdbtools_found=yes/no
+dnl    mdbtools_found=yes (from where)/no
 dnl
 dnl   and if the mdbtools package is found:
 dnl
@@ -171,7 +171,7 @@ int main() {
 	then
 	    AC_MSG_NOTICE([MDB backend not used])
 	else
-    	    mdbtools_found=yes
+    	    mdbtools_found="yes (from system installation)"
 
   	    save_CFLAGS="$CFLAGS"
 	    CFLAGS="$CFLAGS $MDB_CFLAGS $mdb_glib_cflags"
@@ -214,7 +214,7 @@ int main() {
 	fi
     fi
 
-    AM_CONDITIONAL(MDB,[test "$mdbtools_found" = "yes"])
+    AM_CONDITIONAL(MDB,[test "$mdbtools_found" != "no"])
     AC_SUBST(MDB_LIBS)
     AC_SUBST(MDB_CFLAGS)
 ])
@@ -225,5 +225,16 @@ dnl   MDBTOOLS_CHECK([libdirname],[GLib's CFLAGS],[GLib's LIBS])
 
 AC_DEFUN([MDBTOOLS_CHECK],
 [
-    _MDBTOOLS_CHECK_INTERNAL([$1],[$2],[$3])
+     AC_ARG_ENABLE(system-mdbtools,
+		AS_HELP_STRING([--enable-system-mdbtools], [Use MDBTools installed on the system [default=yes]]),
+        	[use_syst_mdb=$enableval], [use_syst_mdb="yes"])
+    if test "x$use_syst_mdb" = "xyes"; then
+        _MDBTOOLS_CHECK_INTERNAL([$1],[$2],[$3])
+    else
+        AM_ICONV
+        AM_CONDITIONAL(MDB, true)
+        AC_DEFINE(MDB_BIND_COLUMN_FOUR_ARGS,[1],[define if mdb_bind_column accepts four args])
+        AC_DEFINE(MDB_WITH_WRITE_SUPPORT,[1],[define if mdb_open accepts MDB_WRITABLE])
+        mdbtools_found="yes (embedded)"
+    fi
 ])
diff --git a/providers/mdb/Makefile.am b/providers/mdb/Makefile.am
index 774c67d..fd997cc 100644
--- a/providers/mdb/Makefile.am
+++ b/providers/mdb/Makefile.am
@@ -1,6 +1,15 @@
 providerdir=$(libdir)/libgda-$(GDA_ABI_MAJOR_VERSION).$(GDA_ABI_MINOR_VERSION)/providers
 provider_LTLIBRARIES = libgda-mdb.la
 
+if PLATFORM_WIN32
+SUBDIRS = libmdb-src
+extra_mdb_cflags = -I$(srcdir)/libmdb-src/include
+extra_mdb_ldflags = libmdb-src/libmdb.la
+else
+extra_mdb_cflags = $(MDB_CFLAGS)
+extra_mdb_ldflags = $(MDB_LIBS)
+endif
+
 AM_CPPFLAGS = \
 	-I$(top_srcdir) \
 	-I$(top_srcdir)/libgda \
@@ -8,7 +17,7 @@ AM_CPPFLAGS = \
 	-I$(top_builddir) \
 	$(LIBGDA_CFLAGS) \
 	$(LIBGDA_WFLAGS) \
-	$(MDB_CFLAGS) \
+	$(extra_mdb_cflags) \
 	-DLIBGDA_DATA_DIR=\""$(datadir)/libgda-4.0"\"
 
 libgda_mdb_la_SOURCES = \
@@ -21,7 +30,7 @@ libgda_mdb_la_LDFLAGS = -export-dynamic -module -avoid-version $(NO_UNDEFINED) $
 libgda_mdb_la_LIBADD = \
 	$(top_builddir)/libgda/libgda-4.0.la \
 	$(LIBGDA_LIBS) \
-	$(MDB_LIBS)
+	$(extra_mdb_ldflags)
 
 xmldir   = $(datadir)/libgda-4.0
 xml_in_files = mdb_specs_dsn.xml.in
diff --git a/providers/mdb/libmdb-src/Makefile.am b/providers/mdb/libmdb-src/Makefile.am
new file mode 100644
index 0000000..3d89645
--- /dev/null
+++ b/providers/mdb/libmdb-src/Makefile.am
@@ -0,0 +1,27 @@
+noinst_LTLIBRARIES = libmdb.la
+
+AM_CPPFLAGS = \
+	-I$(srcdir)/include \
+	$(LIBGDA_CFLAGS) \
+        $(LIBGDA_WFLAGS)
+
+libmdb_la_SOURCES = \
+	catalog.c \
+	mem.c \
+	file.c \
+	kkd.c \
+	table.c \
+	data.c \
+	dump.c \
+	backend.c \
+	money.c \
+	sargs.c \
+	index.c \
+	like.c \
+	write.c \
+	stats.c \
+	map.c \
+	props.c \
+	worktable.c \
+	options.c \
+	iconv.c
diff --git a/providers/mdb/libmdb-src/backend.c b/providers/mdb/libmdb-src/backend.c
new file mode 100644
index 0000000..4ee0647
--- /dev/null
+++ b/providers/mdb/libmdb-src/backend.c
@@ -0,0 +1,308 @@
+/* MDB Tools - A library for reading MS Access database files
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef JAVA
+#include "javadefines.h"
+#define MdbBackendType_STRUCT_ELEMENT(a,b,c,d) new MdbBackendType(a,b,c,d)
+#else
+#define MdbBackendType_STRUCT_ELEMENT(a,b,c,d) {a,b,c,d}
+/*
+** functions to deal with different backend database engines
+*/
+#
+#include "mdbtools.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+#endif   /* JAVA */
+
+static int is_init;
+GHashTable *mdb_backends;
+
+   /*    Access data types */
+static MdbBackendType mdb_access_types[] = {
+		MdbBackendType_STRUCT_ELEMENT("Unknown 0x00", 0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Boolean", 0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Byte", 0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Integer", 0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Long Integer", 0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Currency", 0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Single", 0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Double", 0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("DateTime (Short)", 0,0,1),
+		MdbBackendType_STRUCT_ELEMENT("Unknown 0x09", 0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Text", 1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("OLE", 1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("Memo/Hyperlink",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("Unknown 0x0d",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Unknown 0x0e",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Replication ID",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Numeric",1,1,0)
+};
+
+/*    Oracle data types */
+static MdbBackendType mdb_oracle_types[] = {
+		MdbBackendType_STRUCT_ELEMENT("Oracle_Unknown 0x00",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("NUMBER",1,0,0),
+		MdbBackendType_STRUCT_ELEMENT("NUMBER",1,0,0),
+		MdbBackendType_STRUCT_ELEMENT("NUMBER",1,0,0),
+		MdbBackendType_STRUCT_ELEMENT("NUMBER",1,0,0),
+		MdbBackendType_STRUCT_ELEMENT("NUMBER",1,0,0),
+		MdbBackendType_STRUCT_ELEMENT("FLOAT",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("FLOAT",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("DATE",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Oracle_Unknown 0x09",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("VARCHAR2",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("BLOB",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("CLOB",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("Oracle_Unknown 0x0d",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Oracle_Unknown 0x0e",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("NUMBER",1,0,0),
+		MdbBackendType_STRUCT_ELEMENT("NUMBER",1,0,0),
+};
+
+/*    Sybase/MSSQL data types */
+static MdbBackendType mdb_sybase_types[] = {
+		MdbBackendType_STRUCT_ELEMENT("Sybase_Unknown 0x00",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("bit",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("char",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("smallint",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("int",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("money",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("real",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("float",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("smalldatetime",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Sybase_Unknown 0x09",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("varchar",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("varbinary",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("text",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("Sybase_Unknown 0x0d",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Sybase_Unknown 0x0e",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Sybase_Replication ID",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("numeric",1,1,0),
+};
+
+/*    Postgres data types */
+static MdbBackendType mdb_postgres_types[] = {
+		MdbBackendType_STRUCT_ELEMENT("Postgres_Unknown 0x00",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Bool",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Int2",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Int4",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Int8",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Money",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Float4",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Float8",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Timestamp",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Postgres_Unknown 0x09",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Char",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("Postgres_Unknown 0x0b",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Postgres_Unknown 0x0c",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Postgres_Unknown 0x0d",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Postgres_Unknown 0x0e",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Serial",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("Postgres_Unknown 0x10",0,0,0),
+};
+/*    MySQL data types */
+static MdbBackendType mdb_mysql_types[] = {
+		MdbBackendType_STRUCT_ELEMENT("Text",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("char",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("int",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("int",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("int",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("float",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("float",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("float",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("date",0,0,1),
+		MdbBackendType_STRUCT_ELEMENT("varchar",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("varchar",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("varchar",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("text",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("blob",0,0,0),
+		MdbBackendType_STRUCT_ELEMENT("text",1,0,1),
+		MdbBackendType_STRUCT_ELEMENT("numeric",1,1,0),
+		MdbBackendType_STRUCT_ELEMENT("numeric",1,1,0),
+};
+#ifndef JAVA
+static gboolean mdb_drop_backend(gpointer key, gpointer value, gpointer data);
+
+char *mdb_get_coltype_string(MdbBackend *backend, int col_type)
+{
+	static char buf[16];
+
+	if (col_type > 0x10 ) {
+   		// return NULL;
+		snprintf(buf,sizeof(buf), "type %04x", col_type);
+		return buf;
+	} else {
+		return backend->types_table[col_type].name;
+	}
+}
+
+int mdb_coltype_takes_length(MdbBackend *backend, int col_type)
+{
+	return backend->types_table[col_type].needs_length;
+}
+
+/**
+ * mdb_init_backends
+ *
+ * Initializes the mdb_backends hash and loads the builtin backends.
+ * Use mdb_remove_backends() to destroy this hash when done.
+ */
+void mdb_init_backends()
+{
+	mdb_backends = g_hash_table_new(g_str_hash, g_str_equal);
+
+	mdb_register_backend(mdb_access_types, "access");
+	mdb_register_backend(mdb_sybase_types, "sybase");
+	mdb_register_backend(mdb_oracle_types, "oracle");
+	mdb_register_backend(mdb_postgres_types, "postgres");
+	mdb_register_backend(mdb_mysql_types, "mysql");
+}
+void mdb_register_backend(MdbBackendType *backend_type, char *backend_name)
+{
+	MdbBackend *backend = (MdbBackend *) g_malloc0(sizeof(MdbBackend));
+	backend->types_table = backend_type;
+	g_hash_table_insert(mdb_backends, backend_name, backend);
+}
+
+/**
+ * mdb_remove_backends
+ *
+ * Removes all entries from and destroys the mdb_backends hash.
+ */
+void mdb_remove_backends()
+{
+	g_hash_table_foreach_remove(mdb_backends, mdb_drop_backend, NULL);
+	g_hash_table_destroy(mdb_backends);
+}
+static gboolean mdb_drop_backend(gpointer key, gpointer value, gpointer data)
+{
+	MdbBackend *backend = (MdbBackend *)value;
+	g_free (backend);
+	return TRUE;
+}
+
+/**
+ * mdb_set_default_backend
+ * @mdb: Handle to open MDB database file
+ * @backend_name: Name of the backend to set as default
+ *
+ * Sets the default backend of the handle @mdb to @backend_name.
+ *
+ * Returns: 1 if successful, 0 if unsuccessful.
+ */
+int mdb_set_default_backend(MdbHandle *mdb, char *backend_name)
+{
+	MdbBackend *backend;
+
+	backend = (MdbBackend *) g_hash_table_lookup(mdb_backends, backend_name);
+	if (backend) {
+		mdb->default_backend = backend;
+		mdb->backend_name = (char *) g_strdup(backend_name);
+		is_init = 0;
+		return 1;
+	} else {
+		return 0;
+	}
+}
+
+/**
+ * mdb_get_relationships
+ * @mdb: Handle to open MDB database file
+ *
+ * Generates relationships by reading the MSysRelationships table.
+ *   'szColumn' contains the column name of the child table.
+ *   'szObject' contains the table name of the child table.
+ *   'szReferencedColumn' contains the column name of the parent table.
+ *   'szReferencedObject' contains the table name of the parent table.
+ *
+ * Returns: a string stating that relationships are not supported for the
+ *   selected backend, or a string containing SQL commands for setting up
+ *   the relationship, tailored for the selected backend.  The caller is
+ *   responsible for freeing this string.
+ */
+char *mdb_get_relationships(MdbHandle *mdb)
+{
+	unsigned int i;
+	gchar *text = NULL;  /* String to be returned */
+	static char *bound[4];  /* Bound values */
+	static MdbTableDef *table;  /* Relationships table */
+	int backend = 0;  /* Backends: 1=oracle */
+
+	if (strncmp(mdb->backend_name,"oracle",6) == 0) {
+		backend = 1;
+	} else {
+		if (is_init == 0) { /* the first time through */
+			is_init = 1;
+			return (char *) g_strconcat(
+				"-- relationships are not supported for ",
+				mdb->backend_name, NULL);
+		} else { /* the second time through */
+			is_init = 0;
+			return NULL;
+		}
+	}
+
+	if (is_init == 0) {
+		table = mdb_read_table_by_name(mdb, "MSysRelationships", MDB_TABLE);
+		if ((!table) || (table->num_rows == 0)) {
+			return NULL;
+		}
+
+		mdb_read_columns(table);
+		for (i=0;i<4;i++) {
+			bound[i] = (char *) g_malloc0(MDB_BIND_SIZE);
+		}
+		mdb_bind_column_by_name(table, "szColumn", bound[0], NULL);
+		mdb_bind_column_by_name(table, "szObject", bound[1], NULL);
+		mdb_bind_column_by_name(table, "szReferencedColumn", bound[2], NULL);
+		mdb_bind_column_by_name(table, "szReferencedObject", bound[3], NULL);
+		mdb_rewind_table(table);
+
+		is_init = 1;
+	}
+	else if (table->cur_row >= table->num_rows) {  /* past the last row */
+		for (i=0;i<4;i++)
+			g_free(bound[i]);
+		is_init = 0;
+		return NULL;
+	}
+
+	if (!mdb_fetch_row(table)) {
+		for (i=0;i<4;i++)
+			g_free(bound[i]);
+		is_init = 0;
+		return NULL;
+	}
+
+	switch (backend) {
+	  case 1:  /* oracle */
+		text = g_strconcat("alter table ", bound[1],
+			" add constraint ", bound[3], "_", bound[1],
+			" foreign key (", bound[0], ")"
+			" references ", bound[3], "(", bound[2], ")", NULL);
+		break;
+	}
+
+	return (char *)text;
+}
+#endif
diff --git a/providers/mdb/libmdb-src/catalog.c b/providers/mdb/libmdb-src/catalog.c
new file mode 100644
index 0000000..8179c56
--- /dev/null
+++ b/providers/mdb/libmdb-src/catalog.c
@@ -0,0 +1,139 @@
+/* MDB Tools - A library for reading MS Access database file
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "mdbtools.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+char *
+mdb_get_objtype_string(int obj_type)
+{
+static char *type_name[] = {"Form",
+			"Table",
+			"Macro",
+			"System Table",
+			"Report",
+			"Query",
+			"Linked Table",
+			"Module",
+			"Relationship",
+			"Unknown 0x09",
+			"Unknown 0x0a",
+			"Database"
+		};
+
+	if (obj_type > 11) {
+		return NULL;
+	} else {
+		return type_name[obj_type]; 
+	}
+}
+
+void mdb_free_catalog(MdbHandle *mdb)
+{
+	unsigned int i;
+
+	if ((!mdb) || (!mdb->catalog)) return;
+	for (i=0; i<mdb->catalog->len; i++)
+		g_free (g_ptr_array_index(mdb->catalog, i));
+	g_ptr_array_free(mdb->catalog, TRUE);
+	mdb->catalog = NULL;
+}
+
+GPtrArray *mdb_read_catalog (MdbHandle *mdb, int objtype)
+{
+	MdbCatalogEntry *entry, msysobj;
+	MdbTableDef *table;
+	char obj_id[256];
+	char obj_name[256];
+	char obj_type[256];
+	char obj_flags[256];
+	int type;
+
+	if (!mdb) return NULL;
+	if (mdb->catalog) mdb_free_catalog(mdb);
+	mdb->catalog = g_ptr_array_new();
+	mdb->num_catalog = 0;
+
+	/* dummy up a catalog entry so we may read the table def */
+	memset(&msysobj, 0, sizeof(MdbCatalogEntry));
+	msysobj.mdb = mdb;
+	msysobj.object_type = MDB_TABLE;
+	msysobj.table_pg = 2;
+	strcpy(msysobj.object_name, "MSysObjects");
+
+	/* mdb_table_dump(&msysobj); */
+
+	table = mdb_read_table(&msysobj);
+	if (!table) return NULL;
+
+	mdb_read_columns(table);
+
+	mdb_bind_column_by_name(table, "Id", obj_id, NULL);
+	mdb_bind_column_by_name(table, "Name", obj_name, NULL);
+	mdb_bind_column_by_name(table, "Type", obj_type, NULL);
+	mdb_bind_column_by_name(table, "Flags", obj_flags, NULL);
+
+	mdb_rewind_table(table);
+
+	while (mdb_fetch_row(table)) {
+		type = atoi(obj_type);
+		if (objtype==MDB_ANY || type == objtype) {
+			// fprintf(stdout, "obj_id: %10ld objtype: %-3d obj_name: %s\n", 
+			// (atol(obj_id) & 0x00FFFFFF), type, obj_name); 
+			entry = (MdbCatalogEntry *) g_malloc0(sizeof(MdbCatalogEntry));
+			entry->mdb = mdb;
+			strcpy(entry->object_name, obj_name);
+			entry->object_type = (type & 0x7F);
+			entry->table_pg = atol(obj_id) & 0x00FFFFFF;
+			entry->flags = atol(obj_flags);
+			mdb->num_catalog++;
+			g_ptr_array_add(mdb->catalog, entry); 
+		}
+	}
+	//mdb_dump_catalog(mdb, MDB_TABLE);
+ 
+	mdb_free_tabledef(table);
+
+	return mdb->catalog;
+}
+
+void 
+mdb_dump_catalog(MdbHandle *mdb, int obj_type)
+{
+	unsigned int i;
+	MdbCatalogEntry *entry;
+
+	mdb_read_catalog(mdb, obj_type);
+	for (i=0;i<mdb->num_catalog;i++) {
+                entry = g_ptr_array_index(mdb->catalog,i);
+		if (obj_type==MDB_ANY || entry->object_type==obj_type) {
+			fprintf(stdout,"Type: %-10s Name: %-18s T pg: %04x KKD pg: %04x row: %2d\n",
+			mdb_get_objtype_string(entry->object_type),
+			entry->object_name,
+			(unsigned int) entry->table_pg,
+			(unsigned int) entry->kkd_pg,
+			entry->kkd_rowid);
+		}
+        }
+	return;
+}
+
diff --git a/providers/mdb/libmdb-src/data.c b/providers/mdb/libmdb-src/data.c
new file mode 100644
index 0000000..789aebd
--- /dev/null
+++ b/providers/mdb/libmdb-src/data.c
@@ -0,0 +1,945 @@
+/* MDB Tools - A library for reading MS Access database file
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "mdbtools.h"
+#include "time.h"
+#include "math.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+#define OFFSET_MASK 0x1fff
+
+char *mdb_money_to_string(MdbHandle *mdb, int start);
+static int _mdb_attempt_bind(MdbHandle *mdb, 
+	MdbColumn *col, unsigned char isnull, int offset, int len);
+static char *mdb_num_to_string(MdbHandle *mdb, int start, int datatype, int prec, int scale);
+static char *mdb_date_to_string(MdbHandle *mdb, int start);
+#ifdef MDB_COPY_OLE
+static size_t mdb_copy_ole(MdbHandle *mdb, void *dest, int start, int size);
+#endif
+
+static char date_fmt[64] = "%x %X";
+
+void mdb_set_date_fmt(const char *fmt)
+{
+		date_fmt[63] = 0; 
+		strncpy(date_fmt, fmt, 63);
+}
+
+void mdb_bind_column(MdbTableDef *table, int col_num, void *bind_ptr, int *len_ptr)
+{
+	MdbColumn *col;
+
+	/* 
+	** the column arrary is 0 based, so decrement to get 1 based parameter 
+	*/
+	col=g_ptr_array_index(table->columns, col_num - 1);
+	
+	if (bind_ptr)
+		col->bind_ptr = bind_ptr;
+	if (len_ptr)
+		col->len_ptr = len_ptr;
+}
+int
+mdb_bind_column_by_name(MdbTableDef *table, gchar *col_name, void *bind_ptr, int *len_ptr)
+{
+	unsigned int i;
+	int col_num = -1;
+	MdbColumn *col;
+	
+	for (i=0;i<table->num_cols;i++) {
+		col=g_ptr_array_index(table->columns,i);
+		if (!strcmp(col->name,col_name)) {
+			col_num = i + 1;
+			if (bind_ptr)
+				col->bind_ptr = bind_ptr;
+			if (len_ptr)
+				col->len_ptr = len_ptr;
+			break;
+		}
+	}
+
+	return col_num;
+}
+
+/**
+ * mdb_find_pg_row
+ * @mdb: Database file handle
+ * @pg_row: Lower byte contains the row number, the upper three contain page
+ * @buf: Pointer for returning a pointer to the page
+ * @off: Pointer for returning an offset to the row
+ * @len: Pointer for returning the length of the row
+ * 
+ * Returns: 0 on success.  1 on failure.
+ */
+int mdb_find_pg_row(MdbHandle *mdb, int pg_row, void **buf, int *off, size_t *len)
+{
+	unsigned int pg = pg_row >> 8;
+	unsigned int row = pg_row & 0xff;
+
+	if (mdb_read_alt_pg(mdb, pg) != mdb->fmt->pg_size)
+		return 1;
+	mdb_swap_pgbuf(mdb);
+	mdb_find_row(mdb, row, off, len);
+	mdb_swap_pgbuf(mdb);
+	*buf = mdb->alt_pg_buf;
+	return 0;
+}
+
+int mdb_find_row(MdbHandle *mdb, int row, int *start, int *len)
+{
+	int rco = mdb->fmt->row_count_offset;
+	int next_start;
+
+	if (row > 1000) return -1;
+
+	*start = mdb_get_int16(mdb->pg_buf, rco + 2 + row*2);
+	next_start = (row == 0) ? mdb->fmt->pg_size :
+		mdb_get_int16(mdb->pg_buf, rco + row*2) & OFFSET_MASK;
+	*len = next_start - (*start & OFFSET_MASK);
+	return 0;
+}
+
+int 
+mdb_find_end_of_row(MdbHandle *mdb, int row)
+{
+	int rco = mdb->fmt->row_count_offset;
+	int row_end;
+
+#if 1
+	if (row > 1000) return -1;
+
+	row_end = (row == 0) ? mdb->fmt->pg_size :
+		mdb_get_int16(mdb->pg_buf, rco + row*2) & OFFSET_MASK;
+#else
+	/* Search the previous "row start" values for the first non-'lookupflag'
+	 * one. If we don't find one, then the end of the page is the correct
+	 * value.
+	 */
+	int i, row_start;
+
+	if (row > 1000) return -1;
+
+	/* if lookupflag is not set, it's good (deleteflag is ok) */
+        for (i = row; i > 0; i--) {
+                row_start = mdb_get_int16(mdb->pg_buf, (rco + i*2));
+                if (!(row_start & 0x8000)) {
+                        break;
+                }
+        }
+
+	row_end = (i == 0) ? mdb->fmt->pg_size : row_start & OFFSET_MASK;
+#endif
+	return row_end - 1;
+}
+int mdb_is_null(unsigned char *null_mask, int col_num)
+{
+int byte_num = (col_num - 1) / 8;
+int bit_num = (col_num - 1) % 8;
+
+	if ((1 << bit_num) & null_mask[byte_num]) {
+		return 0;
+	} else {
+		return 1;
+	}
+}
+/* bool has to be handled specially because it uses the null bit to store its 
+** value*/
+static size_t 
+mdb_xfer_bound_bool(MdbHandle *mdb, MdbColumn *col, int value)
+{
+	col->cur_value_len = value;
+	if (col->bind_ptr) {
+		strcpy(col->bind_ptr, value ? "0" : "1");
+	}
+	if (col->len_ptr) {
+		*col->len_ptr = 1;
+	}
+
+	return 1;
+}
+static size_t
+mdb_xfer_bound_ole(MdbHandle *mdb, int start, MdbColumn *col, int len)
+{
+	size_t ret = 0;
+	if (len) {
+		col->cur_value_start = start;
+		col->cur_value_len = len;
+	} else {
+		col->cur_value_start = 0;
+		col->cur_value_len = 0;
+	}
+#ifdef MDB_COPY_OLE
+	if (col->bind_ptr || col->len_ptr) {
+		ret = mdb_copy_ole(mdb, col->bind_ptr, start, len);
+	}
+#else
+	if (col->bind_ptr) {
+		memcpy(col->bind_ptr, mdb->pg_buf + start, MDB_MEMO_OVERHEAD);
+	}
+	ret = MDB_MEMO_OVERHEAD;
+#endif
+	if (col->len_ptr) {
+		*col->len_ptr = ret;
+	}
+	return ret;
+}
+static size_t
+mdb_xfer_bound_data(MdbHandle *mdb, int start, MdbColumn *col, int len)
+{
+int ret;
+	//if (!strcmp("Name",col->name)) {
+		//printf("start %d %d\n",start, len);
+	//}
+	if (len) {
+		col->cur_value_start = start;
+		col->cur_value_len = len;
+	} else {
+		col->cur_value_start = 0;
+		col->cur_value_len = 0;
+	}
+	if (col->bind_ptr) {
+		if (!len) {
+			strcpy(col->bind_ptr, "");
+		} else {
+			//fprintf(stdout,"len %d size %d\n",len, col->col_size);
+			char *str;
+			if (col->col_type == MDB_NUMERIC) {
+				str = mdb_num_to_string(mdb, start,
+					col->col_type, col->col_prec,
+					col->col_scale);
+			} else {
+				str = mdb_col_to_string(mdb, mdb->pg_buf, start,
+					col->col_type, len);
+			}
+			strcpy(col->bind_ptr, str);
+			g_free(str);
+		}
+		ret = strlen(col->bind_ptr);
+		if (col->len_ptr) {
+			*col->len_ptr = ret;
+		}
+		return ret;
+	}
+	return 0;
+}
+int mdb_read_row(MdbTableDef *table, unsigned int row)
+{
+	MdbHandle *mdb = table->entry->mdb;
+	MdbColumn *col;
+	unsigned int i;
+	int rc;
+	int row_start, row_size;
+	int delflag, lookupflag;
+	MdbField fields[256];
+	int num_fields;
+
+	if (table->num_rows == 0) 
+		return 0;
+
+	mdb_find_row(mdb, row, &row_start, &row_size);
+
+	delflag = lookupflag = 0;
+	if (row_start & 0x8000) lookupflag++;
+	if (row_start & 0x4000) delflag++;
+	row_start &= OFFSET_MASK; /* remove flags */
+#if MDB_DEBUG
+	fprintf(stdout,"Row %d bytes %d to %d %s %s\n", 
+		row, row_start, row_start + row_size - 1,
+		lookupflag ? "[lookup]" : "",
+		delflag ? "[delflag]" : "");
+#endif	
+
+	if (!table->noskip_del && delflag) {
+		return 0;
+	}
+
+	num_fields = mdb_crack_row(table, row_start, row_start + row_size - 1,
+		fields);
+	if (!mdb_test_sargs(table, fields, num_fields)) return 0;
+	
+#if MDB_DEBUG
+	fprintf(stdout,"sarg test passed row %d \n", row);
+#endif 
+
+#if MDB_DEBUG
+	buffer_dump(mdb->pg_buf, row_start, row_start + row_size - 1);
+#endif
+
+	/* take advantage of mdb_crack_row() to clean up binding */
+	/* use num_cols instead of num_fields -- bsb 03/04/02 */
+	for (i = 0; i < table->num_cols; i++) {
+		col = g_ptr_array_index(table->columns,fields[i].colnum);
+		rc = _mdb_attempt_bind(mdb, col, fields[i].is_null,
+			fields[i].start, fields[i].siz);
+	}
+
+	return 1;
+}
+static int _mdb_attempt_bind(MdbHandle *mdb, 
+	MdbColumn *col, 
+	unsigned char isnull, 
+	int offset, 
+	int len)
+{
+	if (col->col_type == MDB_BOOL) {
+		mdb_xfer_bound_bool(mdb, col, isnull);
+	} else if (isnull) {
+		mdb_xfer_bound_data(mdb, 0, col, 0);
+	} else if (col->col_type == MDB_OLE) {
+		mdb_xfer_bound_ole(mdb, offset, col, len);
+	} else {
+		//if (!mdb_test_sargs(mdb, col, offset, len)) {
+			//return 0;
+		//}
+		mdb_xfer_bound_data(mdb, offset, col, len);
+	}
+	return 1;
+}
+int mdb_read_next_dpg(MdbTableDef *table)
+{
+	MdbCatalogEntry *entry = table->entry;
+	MdbHandle *mdb = entry->mdb;
+	int next_pg;
+
+#ifndef SLOW_READ
+	next_pg = mdb_map_find_next(mdb, table->usage_map,
+		table->map_sz, table->cur_phys_pg);
+
+	if (next_pg >= 0) {
+		if (mdb_read_pg(mdb, next_pg)) {
+			table->cur_phys_pg = next_pg;
+			return table->cur_phys_pg;
+		} else {
+			return 0;
+		}
+	}
+	fprintf(stderr, "Warning: defaulting to brute force read\n");
+#endif 
+	/* can't do a fast read, go back to the old way */
+	do {
+		if (!mdb_read_pg(mdb, table->cur_phys_pg++))
+			return 0;
+	} while (mdb->pg_buf[0]!=0x01 || mdb_get_int32(mdb->pg_buf, 4)!=entry->table_pg);
+	/* fprintf(stderr,"returning new page %ld\n", table->cur_phys_pg); */
+	return table->cur_phys_pg;
+}
+int mdb_rewind_table(MdbTableDef *table)
+{
+	table->cur_pg_num=0;
+	table->cur_phys_pg=0;
+	table->cur_row=0;
+
+	return 0;
+}
+int 
+mdb_fetch_row(MdbTableDef *table)
+{
+	MdbHandle *mdb = table->entry->mdb;
+	MdbFormatConstants *fmt = mdb->fmt;
+	unsigned int rows;
+	int rc;
+	guint32 pg;
+
+	if (table->num_rows==0)
+		return 0;
+
+	/* initialize */
+	if (!table->cur_pg_num) {
+		table->cur_pg_num=1;
+		table->cur_row=0;
+		if ((!table->is_temp_table)&&(table->strategy!=MDB_INDEX_SCAN))
+			if (!mdb_read_next_dpg(table)) return 0;
+	}
+
+	do {
+		if (table->is_temp_table) {
+			GPtrArray *pages = table->temp_table_pages;
+			rows = mdb_get_int16(
+				g_ptr_array_index(pages, table->cur_pg_num-1),
+				fmt->row_count_offset);
+			if (table->cur_row >= rows) {
+				table->cur_row = 0;
+				table->cur_pg_num++;
+				if (table->cur_pg_num > pages->len)
+					return 0;
+			}
+			memcpy(mdb->pg_buf,
+				g_ptr_array_index(pages, table->cur_pg_num-1),
+				fmt->pg_size);
+		} else if (table->strategy==MDB_INDEX_SCAN) {
+		
+			if (!mdb_index_find_next(table->mdbidx, table->scan_idx, table->chain, &pg, (guint16 *) &(table->cur_row))) {
+				mdb_index_scan_free(table);
+				return 0;
+			}
+			mdb_read_pg(mdb, pg);
+		} else {
+			rows = mdb_get_int16(mdb->pg_buf,fmt->row_count_offset);
+
+			/* if at end of page, find a new page */
+			if (table->cur_row >= rows) {
+				table->cur_row=0;
+	
+				if (!mdb_read_next_dpg(table)) {
+					return 0;
+				}
+			}
+		}
+
+		/* printf("page %d row %d\n",table->cur_phys_pg, table->cur_row); */
+		rc = mdb_read_row(table, table->cur_row);
+		table->cur_row++;
+	} while (!rc);
+
+	return 1;
+}
+void mdb_data_dump(MdbTableDef *table)
+{
+	unsigned int i;
+	char *bound_values[MDB_MAX_COLS]; 
+
+	for (i=0;i<table->num_cols;i++) {
+		bound_values[i] = (char *) g_malloc(256);
+		mdb_bind_column(table, i+1, bound_values[i], NULL);
+	}
+	mdb_rewind_table(table);
+	while (mdb_fetch_row(table)) {
+		for (i=0;i<table->num_cols;i++) {
+			fprintf(stdout, "column %d is %s\n", i+1, bound_values[i]);
+		}
+	}
+	for (i=0;i<table->num_cols;i++) {
+		g_free(bound_values[i]);
+	}
+}
+
+int mdb_is_fixed_col(MdbColumn *col)
+{
+	return col->is_fixed;
+}
+#if 0
+static char *mdb_data_to_hex(MdbHandle *mdb, char *text, int start, int size) 
+{
+int i;
+
+	for (i=start; i<start+size; i++) {
+		sprintf(&text[(i-start)*2],"%02x", mdb->pg_buf[i]);
+	}
+	text[(i-start)*2]='\0';
+
+	return text;
+}
+#endif
+size_t 
+mdb_ole_read_next(MdbHandle *mdb, MdbColumn *col, void *ole_ptr)
+{
+	guint32 ole_len;
+	void *buf;
+	int row_start;
+	size_t len;
+
+	ole_len = mdb_get_int32(ole_ptr, 0);
+
+	if ((ole_len & 0x80000000)
+	 || (ole_len & 0x40000000)) {
+		/* inline or single-page fields don't have a next */
+		return 0;
+	} else {
+		if (mdb_find_pg_row(mdb, col->cur_blob_pg_row,
+			&buf, &row_start, &len)) {
+			return 0;
+		}
+		if (col->bind_ptr)
+			memcpy(col->bind_ptr, buf + row_start + 4, len - 4);
+		col->cur_blob_pg_row = mdb_get_int32(buf, row_start);
+
+		return len;
+	}
+	return 0;
+}
+size_t 
+mdb_ole_read(MdbHandle *mdb, MdbColumn *col, void *ole_ptr, int chunk_size)
+{
+	guint32 ole_len;
+	void *buf;
+	int row_start;
+	size_t len;
+
+	ole_len = mdb_get_int32(ole_ptr, 0);
+	mdb_debug(MDB_DEBUG_OLE,"ole len = %d ole flags = %02x",
+		ole_len & 0x00ffffff, ole_len >> 24);
+
+	col->chunk_size = chunk_size;
+
+	if (ole_len & 0x80000000) {
+		/* inline ole field, if we can satisfy it, then do it */
+		len = col->cur_value_len - MDB_MEMO_OVERHEAD;
+		if (chunk_size >= len) {
+			if (col->bind_ptr) 
+				memcpy(col->bind_ptr, 
+					&mdb->pg_buf[col->cur_value_start + 
+						MDB_MEMO_OVERHEAD],
+					len);
+			return len;
+		} else {
+			return 0;
+		}
+	} else if (ole_len & 0x40000000) {
+		col->cur_blob_pg_row = mdb_get_int32(ole_ptr, 4);
+		mdb_debug(MDB_DEBUG_OLE,"ole row = %d ole pg = %ld",
+			col->cur_blob_pg_row & 0xff,
+			col->cur_blob_pg_row >> 8);
+
+		if (mdb_find_pg_row(mdb, col->cur_blob_pg_row,
+			&buf, &row_start, &len)) {
+			return 0;
+		}
+		mdb_debug(MDB_DEBUG_OLE,"start %d len %d", row_start, len);
+
+		if (col->bind_ptr) {
+			memcpy(col->bind_ptr, buf + row_start, len);
+			if (mdb_get_option(MDB_DEBUG_OLE))
+				buffer_dump(col->bind_ptr, 0, 16);
+		}
+		return len;
+	} else if ((ole_len & 0xff000000) == 0) {
+		col->cur_blob_pg_row = mdb_get_int32(ole_ptr, 4);
+
+		if (mdb_find_pg_row(mdb, col->cur_blob_pg_row,
+			&buf, &row_start, &len)) {
+			return 0;
+		}
+		if (col->bind_ptr) 
+			memcpy(col->bind_ptr, buf + row_start + 4, len - 4);
+		col->cur_blob_pg_row = mdb_get_int32(buf, row_start);
+
+		return len;
+	} else {
+		fprintf(stderr,"Unhandled ole field flags = %02x\n", ole_len >> 24);
+		return 0;
+	}
+}
+#ifdef MDB_COPY_OLE
+static size_t mdb_copy_ole(MdbHandle *mdb, void *dest, int start, int size)
+{
+	guint32 ole_len;
+	guint32 row_start, pg_row;
+	size_t len;
+	void *buf, *pg_buf = mdb->pg_buf;
+
+	if (size<MDB_MEMO_OVERHEAD) {
+		return 0;
+	} 
+
+	/* The 16 bit integer at offset 0 is the length of the memo field.
+	 * The 32 bit integer at offset 4 contains page and row information.
+	 */
+	ole_len = mdb_get_int32(pg_buf, start);
+
+	if (ole_len & 0x80000000) {
+		/* inline */
+		len = size - MDB_MEMO_OVERHEAD;
+		if (dest) memcpy(dest, pg_buf + start + MDB_MEMO_OVERHEAD, len);
+		return len;
+	} else if (ole_len & 0x40000000) {
+		/* single page */
+		pg_row = mdb_get_int32(pg_buf, start+4);
+		mdb_debug(MDB_DEBUG_OLE,"Reading LVAL page %06x", pg_row >> 8);
+
+		if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &len)) {
+			return 0;
+		}
+		mdb_debug(MDB_DEBUG_OLE,"row num %d start %d len %d",
+			pg_row & 0xff, row_start, len);
+
+		if (dest)
+			memcpy(dest, buf + row_start, len);
+		return len;
+	} else if ((ole_len & 0xff000000) == 0) { // assume all flags in MSB
+		/* multi-page */
+		int cur = 0;
+		pg_row = mdb_get_int32(pg_buf, start+4);
+		do {
+			mdb_debug(MDB_DEBUG_OLE,"Reading LVAL page %06x",
+				pg_row >> 8);
+
+			if (mdb_find_pg_row(mdb,pg_row,&buf,&row_start,&len)) {
+				return 0;
+			}
+
+			mdb_debug(MDB_DEBUG_OLE,"row num %d start %d len %d",
+				pg_row & 0xff, row_start, len);
+
+			if (dest) 
+				memcpy(dest+cur, buf + row_start + 4, len - 4);
+			cur += len - 4;
+
+			/* find next lval page */
+			pg_row = mdb_get_int32(buf, row_start);
+		} while ((pg_row >> 8));
+		return cur;
+	} else {
+		fprintf(stderr, "Unhandled ole field flags = %02x\n", ole_len >> 24);
+		return 0;
+	}
+}
+#endif
+static char *mdb_memo_to_string(MdbHandle *mdb, int start, int size)
+{
+	guint32 memo_len;
+	guint32 row_start, pg_row;
+	size_t len;
+	void *buf, *pg_buf = mdb->pg_buf;
+	char *text = (char *) g_malloc(MDB_BIND_SIZE);
+
+	if (size<MDB_MEMO_OVERHEAD) {
+		strcpy(text, "");
+		return text;
+	} 
+
+#if MDB_DEBUG
+	buffer_dump(pg_buf, start, start + MDB_MEMO_OVERHEAD - 1);
+#endif
+
+	/* The 32 bit integer at offset 0 is the length of the memo field
+	 *   with some flags in the high bits.
+	 * The 32 bit integer at offset 4 contains page and row information.
+	 */
+	memo_len = mdb_get_int32(pg_buf, start);
+
+	if (memo_len & 0x80000000) {
+		/* inline memo field */
+		mdb_unicode2ascii(mdb, pg_buf + start + MDB_MEMO_OVERHEAD,
+			size - MDB_MEMO_OVERHEAD, text, MDB_BIND_SIZE);
+		return text;
+	} else if (memo_len & 0x40000000) {
+		/* single-page memo field */
+		pg_row = mdb_get_int32(pg_buf, start+4);
+#if MDB_DEBUG
+		printf("Reading LVAL page %06x\n", pg_row >> 8);
+#endif
+		if (mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &len)) {
+			strcpy(text, "");
+			return text;
+		}
+#if MDB_DEBUG
+		printf("row num %d start %d len %d\n",
+			pg_row & 0xff, row_start, len);
+		buffer_dump(buf, row_start, row_start + len - 1);
+#endif
+		mdb_unicode2ascii(mdb, buf + row_start, len, text, MDB_BIND_SIZE);
+		return text;
+	} else if ((memo_len & 0xff000000) == 0) { // assume all flags in MSB
+		/* multi-page memo field */
+		guint32 tmpoff = 0;
+		char *tmp;
+
+		tmp = (char *) g_malloc(memo_len);
+		pg_row = mdb_get_int32(pg_buf, start+4);
+		do {
+#if MDB_DEBUG
+			printf("Reading LVAL page %06x\n", pg_row >> 8);
+#endif
+			if (mdb_find_pg_row(mdb,pg_row,&buf,&row_start,&len)) {
+				g_free(tmp);
+				strcpy(text, "");
+				return text;
+			}
+#if MDB_DEBUG
+			printf("row num %d start %d len %d\n",
+				pg_row & 0xff, row_start, len);
+#endif
+			if (tmpoff + len - 4 > memo_len) {
+				break;
+			}
+			memcpy(tmp + tmpoff, buf + row_start + 4, len - 4);
+			tmpoff += len - 4;
+		} while (( pg_row = mdb_get_int32(buf, row_start) ));
+		if (tmpoff < memo_len) {
+			fprintf(stderr, "Warning: incorrect memo length\n");
+		}
+		mdb_unicode2ascii(mdb, tmp, tmpoff, text, MDB_BIND_SIZE);
+		g_free(tmp);
+		return text;
+	} else {
+		fprintf(stderr, "Unhandled memo field flags = %02x\n", memo_len >> 24);
+		strcpy(text, "");
+		return text;
+	}
+}
+static char *
+mdb_num_to_string(MdbHandle *mdb, int start, int datatype, int prec, int scale)
+{
+	char *text;
+	gint32 l;
+
+	memcpy(&l, mdb->pg_buf+start+13, 4);
+
+	text = (char *) g_malloc(prec+2);
+	sprintf(text, "%0*" G_GINT32_FORMAT, prec, GINT32_FROM_LE(l));
+	if (scale) {
+		memmove(text+prec-scale, text+prec-scale+1, scale+1);
+		text[prec-scale] = '.';
+	}
+	return text;
+}
+
+static int trim_trailing_zeros(char * buff)
+{
+	char *p;
+	int n = strlen(buff);
+
+	/* Don't need to trim strings with no decimal portion */
+	if(!strchr(buff,'.'))
+		return 0;
+
+	/* Trim the zeros */
+	p = buff + n - 1;
+	while (p >= buff && *p == '0')
+		*p-- = '\0';
+
+	/* If a decimal sign is left at the end, remove it too */
+	if (*p == '.')
+		*p = '\0';
+
+	return 0;
+}
+
+/* Date/Time is stored as a double, where the whole
+   part is the days from 12/30/1899 and the fractional
+   part is the fractional part of one day. */
+static char *
+mdb_date_to_string(MdbHandle *mdb, int start)
+{
+	struct tm t;
+	long int day, time;
+	int yr, q;
+	int *cal;
+	int noleap_cal[] = {0,31,59,90,120,151,181,212,243,273,304,334,365};
+	int leap_cal[]   = {0,31,60,91,121,152,182,213,244,274,305,335,366};
+
+	char *text = (char *) g_malloc(MDB_BIND_SIZE);
+	double td = mdb_get_double(mdb->pg_buf, start);
+
+	day = (long int)(td);
+	time = (long int)(fabs(td - day) * 86400.0 + 0.5);
+	t.tm_hour = time / 3600;
+	t.tm_min = (time / 60) % 60;
+	t.tm_sec = time % 60; 
+	t.tm_year = 1 - 1900;
+
+	day += 693593; /* Days from 1/1/1 to 12/31/1899 */
+	t.tm_wday = (day+1) % 7;
+
+	q = day / 146097;  /* 146097 days in 400 years */
+	t.tm_year += 400 * q;
+	day -= q * 146097;
+
+	q = day / 36524;  /* 36524 days in 100 years */
+	if (q > 3) q = 3;
+	t.tm_year += 100 * q;
+	day -= q * 36524;
+
+	q = day / 1461;  /* 1461 days in 4 years */
+	t.tm_year += 4 * q;
+	day -= q * 1461;
+
+	q = day / 365;  /* 365 days in 1 year */
+	if (q > 3) q = 3;
+	t.tm_year += q;
+	day -= q * 365;
+
+	yr = t.tm_year + 1900;
+	cal = ((yr)%4==0 && ((yr)%100!=0 || (yr)%400==0)) ?
+		leap_cal : noleap_cal;
+	for (t.tm_mon=0; t.tm_mon<12; t.tm_mon++) {
+		if (day < cal[t.tm_mon+1]) break;
+	}
+	t.tm_mday = day - cal[t.tm_mon] + 1;
+	t.tm_yday = day;
+	t.tm_isdst = -1;
+
+	strftime(text, MDB_BIND_SIZE, date_fmt, &t);
+
+	return text;
+}
+
+int floor_log10(double f, int is_single)
+{
+	unsigned int i;
+	double y = 10.0;
+
+	if (f < 0.0)
+		f = -f;
+	
+	if ((f == 0.0) || (f == 1.0) || isinf(f)) {
+		return 0;
+	} else if (f < 1.0) {
+		if (is_single) {
+			/* The intermediate value p is necessary to prevent
+			 * promotion of the comparison to type double */
+			float p;
+			for (i=1; (p = f * y) < 1.0; i++)
+				y *= 10.0;
+		} else {
+			for (i=1; f * y < 1.0; i++)
+				y *= 10.0;
+		}
+		return -(int)i;
+	} else {  /* (x > 1.0) */
+		for (i=0; f >= y; i++)
+			y *= 10.0;
+		return (int)i;
+	}
+}
+
+char *mdb_col_to_string(MdbHandle *mdb, unsigned char *buf, int start, int datatype, int size)
+{
+	char *text;
+	float tf;
+	double td;
+
+	switch (datatype) {
+		case MDB_BOOL:
+			/* shouldn't happen.  bools are handled specially
+			** by mdb_xfer_bound_bool() */
+		break;
+		case MDB_BYTE:
+			text = g_strdup_printf("%d", mdb_get_byte(buf, start));
+		break;
+		case MDB_INT:
+			text = g_strdup_printf("%ld",
+				(long)mdb_get_int16(buf, start));
+		break;
+		case MDB_LONGINT:
+			text = g_strdup_printf("%ld",
+				mdb_get_int32(buf, start));
+		break;
+		case MDB_FLOAT:
+			tf = mdb_get_single(mdb->pg_buf, start);
+			text = g_strdup_printf("%.*f",
+				FLT_DIG - floor_log10(tf,1) - 1, tf);
+			trim_trailing_zeros(text);
+		break;
+		case MDB_DOUBLE:
+			td = mdb_get_double(mdb->pg_buf, start);
+			text = g_strdup_printf("%.*f",
+				DBL_DIG - floor_log10(td,0) - 1, td);
+			trim_trailing_zeros(text);
+		break;
+		case MDB_TEXT:
+			if (size<0) {
+				text = g_strdup("");
+			} else {
+				text = (char *) g_malloc(MDB_BIND_SIZE);
+				mdb_unicode2ascii(mdb, mdb->pg_buf + start,
+					size, text, MDB_BIND_SIZE);
+			}
+		break;
+		case MDB_SDATETIME:
+			text = mdb_date_to_string(mdb, start);
+		break;
+		case MDB_MEMO:
+			text = mdb_memo_to_string(mdb, start, size);
+		break;
+		case MDB_MONEY:
+			text = mdb_money_to_string(mdb, start);
+		case MDB_NUMERIC:
+		break;
+		default:
+			text = g_strdup("");
+		break;
+	}
+	return text;
+}
+int mdb_col_disp_size(MdbColumn *col)
+{
+	switch (col->col_type) {
+		case MDB_BOOL:
+			return 1;
+		break;
+		case MDB_BYTE:
+			return 4;
+		break;
+		case MDB_INT:
+			return 6;
+		break;
+		case MDB_LONGINT:
+			return 11;
+		break;
+		case MDB_FLOAT:
+			return 10;
+		break;
+		case MDB_DOUBLE:
+			return 10;
+		break;
+		case MDB_TEXT:
+			return col->col_size;
+		break;
+		case MDB_SDATETIME:
+			return 20;
+		break;
+		case MDB_MEMO:
+			return 64000; 
+		break;
+		case MDB_MONEY:
+			return 21;
+		break;
+	}
+	return 0;
+}
+int mdb_col_fixed_size(MdbColumn *col)
+{
+	switch (col->col_type) {
+		case MDB_BOOL:
+			return 1;
+		break;
+		case MDB_BYTE:
+			return -1;
+		break;
+		case MDB_INT:
+			return 2;
+		break;
+		case MDB_LONGINT:
+			return 4;
+		break;
+		case MDB_FLOAT:
+			return 4;
+		break;
+		case MDB_DOUBLE:
+			return 8;
+		break;
+		case MDB_TEXT:
+			return -1;
+		break;
+		case MDB_SDATETIME:
+			return 4;
+		break;
+		case MDB_MEMO:
+			return -1; 
+		break;
+		case MDB_MONEY:
+			return 8;
+		break;
+	}
+	return 0;
+}
diff --git a/providers/mdb/libmdb-src/dump.c b/providers/mdb/libmdb-src/dump.c
new file mode 100644
index 0000000..7ee17f9
--- /dev/null
+++ b/providers/mdb/libmdb-src/dump.c
@@ -0,0 +1,39 @@
+#include <ctype.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+void buffer_dump(const unsigned char* buf, int start, int end)
+{
+	char asc[20];
+	int j, k;
+
+	memset(asc, 0, sizeof(asc));
+	k = 0;
+	for (j=start; j<=end; j++) {
+		if (k == 0) {
+			fprintf(stdout, "%04x  ", j);
+		}
+		fprintf(stdout, "%02x ", buf[j]);
+		asc[k] = isprint(buf[j]) ? buf[j] : '.';
+		k++;
+		if (k == 8) {
+			fprintf(stdout, " ");
+		}
+		if (k == 16) {
+			fprintf(stdout, "  %s\n", asc);
+			memset(asc, 0, sizeof(asc));
+			k = 0;
+		}
+	}
+	for (j=k; j<16; j++) {
+		fprintf(stdout, "   ");
+	}
+	if (k < 8) {
+		fprintf(stdout, " ");
+	}
+	fprintf(stdout, "  %s\n", asc);
+}
diff --git a/providers/mdb/libmdb-src/file.c b/providers/mdb/libmdb-src/file.c
new file mode 100644
index 0000000..fe18355
--- /dev/null
+++ b/providers/mdb/libmdb-src/file.c
@@ -0,0 +1,389 @@
+/* MDB Tools - A library for reading MS Access database files
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "mdbtools.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+/*
+typedef struct {
+	int		pg_size;
+	guint16		row_count_offset; 
+	guint16		tab_num_rows_offset;
+	guint16		tab_num_cols_offset;
+	guint16		tab_num_idxs_offset;
+	guint16		tab_num_ridxs_offset;
+	guint16		tab_usage_map_offset;
+	guint16		tab_first_dpg_offset;
+	guint16		tab_cols_start_offset;
+	guint16		tab_ridx_entry_size;
+	guint16		col_fixed_offset;
+	guint16		col_size_offset;
+	guint16		col_num_offset;
+	guint16		tab_col_entry_size;
+	guint16         tab_free_map_offset;
+	guint16         tab_col_offset_var;
+	guint16         tab_col_offset_fixed;
+	guint16         tab_row_col_num_offset;
+} MdbFormatConstants; 
+*/
+MdbFormatConstants MdbJet4Constants = {
+	4096, 0x0c, 16, 45, 47, 51, 55, 56, 63, 12, 15, 23, 5, 25, 59, 7, 21, 9
+};
+MdbFormatConstants MdbJet3Constants = {
+	2048, 0x08, 12, 25, 27, 31, 35, 36, 43, 8, 13, 16, 1, 18, 39, 3, 14, 5
+};
+
+static ssize_t _mdb_read_pg(MdbHandle *mdb, unsigned char *pg_buf, unsigned long pg);
+
+/**
+ * mdb_find_file:
+ * @filename: path to MDB (database) file
+ *
+ * Finds and returns the absolute path to an MDB file.  Function will first try
+ * to fstat file as passed, then search through the $MDBPATH if not found.
+ *
+ * Return value: gchar pointer to absolute path. Caller is responsible for
+ * freeing.
+ **/
+
+static gchar *mdb_find_file(const char *file_name)
+{
+	struct stat status;
+	gchar *mdbpath, **dir, *tmpfname;
+	unsigned int i = 0;
+
+	/* try the provided file name first */
+	if (!stat(file_name, &status)) {
+		return g_strdup(file_name);
+	}
+	
+	/* Now pull apart $MDBPATH and try those */
+	mdbpath = (gchar *) getenv("MDBPATH");
+	/* no path, can't find file */
+	if (!mdbpath || !strlen(mdbpath)) return NULL;
+
+	dir = g_strsplit(mdbpath, ":", 0); 
+	while (dir[i]) {
+		if (!strlen(dir[i])) continue;
+		tmpfname = g_strconcat(dir[i++], "/", file_name, NULL);
+		if (!stat(tmpfname, &status)) {
+			g_strfreev(dir);
+			return tmpfname;
+		}
+		g_free(tmpfname);
+	}
+	g_strfreev(dir);
+	return NULL;
+}
+/**
+ * mdb_open:
+ * @filename: path to MDB (database) file
+ * @flags: MDB_NOFLAGS for read-only, MDB_WRITABLE for read/write
+ *
+ * Opens an MDB file and returns an MdbHandle to it.  MDB File may be relative
+ * to the current directory, a full path to the file, or relative to a 
+ * component of $MDBPATH.
+ *
+ * Return value: pointer to MdbHandle structure.
+ **/
+MdbHandle *mdb_open(const char *filename, MdbFileFlags flags)
+{
+	MdbHandle *mdb;
+	int open_flags;
+
+	mdb = (MdbHandle *) g_malloc0(sizeof(MdbHandle));
+	mdb_set_default_backend(mdb, "access");
+#ifdef HAVE_ICONV
+	mdb->iconv_in = (iconv_t)-1;
+	mdb->iconv_out = (iconv_t)-1;
+#endif
+	/* need something to bootstrap with, reassign after page 0 is read */
+	mdb->fmt = &MdbJet3Constants;
+	mdb->f = (MdbFile *) g_malloc0(sizeof(MdbFile));
+	mdb->f->refs = 1;
+	mdb->f->fd = -1;
+	mdb->f->filename = (char *) mdb_find_file(filename);
+	if (!mdb->f->filename) { 
+		fprintf(stderr, "Can't alloc filename\n");
+		mdb_close(mdb);
+		return NULL; 
+	}
+	if (flags & MDB_WRITABLE) {
+		mdb->f->writable = TRUE;
+		open_flags = O_RDWR;
+	} else {
+		open_flags = O_RDONLY;
+	}
+
+#ifdef _WIN32
+	open_flags |= O_BINARY;
+#endif
+
+	mdb->f->fd = open(mdb->f->filename, open_flags);
+
+	if (mdb->f->fd==-1) {
+		fprintf(stderr,"Couldn't open file %s\n",mdb->f->filename); 
+		mdb_close(mdb);
+		return NULL;
+	}
+	if (!mdb_read_pg(mdb, 0)) {
+		fprintf(stderr,"Couldn't read first page.\n");
+		mdb_close(mdb);
+		return NULL;
+	}
+	if (mdb->pg_buf[0] != 0) {
+		mdb_close(mdb);
+		return NULL; 
+	}
+	mdb->f->jet_version = mdb_get_int32(mdb->pg_buf, 0x14);
+	if (IS_JET4(mdb)) {
+		mdb->fmt = &MdbJet4Constants;
+	} else if (IS_JET3(mdb)) {
+		mdb->fmt = &MdbJet3Constants;
+	} else {
+		fprintf(stderr,"Unknown Jet version.\n");
+		mdb_close(mdb);
+		return NULL; 
+	}
+	mdb_iconv_init(mdb);
+
+	return mdb;
+}
+
+/**
+ * mdb_close:
+ * @mdb: Handle to open MDB database file
+ *
+ * Dereferences MDB file, closes if reference count is 0, and destroys handle.
+ *
+ **/
+void 
+mdb_close(MdbHandle *mdb)
+{
+	if (!mdb) return;	
+	mdb_free_catalog(mdb);
+	g_free(mdb->stats);
+	g_free(mdb->backend_name);
+
+	if (mdb->f) {
+		if (mdb->f->refs > 1) {
+			mdb->f->refs--;
+		} else {
+			if (mdb->f->fd != -1) close(mdb->f->fd);
+			g_free(mdb->f->filename);
+			g_free(mdb->f);
+		}
+	}
+
+	mdb_iconv_close(mdb);
+
+	g_free(mdb);
+}
+/**
+ * mdb_clone_handle:
+ * @mdb: Handle to open MDB database file
+ *
+ * Clones an existing database handle.  Cloned handle shares the file descriptor
+ * but has its own page buffer, page position, and similar internal variables.
+ *
+ * Return value: new handle to the database.
+ */
+MdbHandle *mdb_clone_handle(MdbHandle *mdb)
+{
+	MdbHandle *newmdb;
+	MdbCatalogEntry *entry, *data;
+	unsigned int i;
+
+	newmdb = (MdbHandle *) g_memdup(mdb, sizeof(MdbHandle));
+	newmdb->stats = NULL;
+	newmdb->catalog = g_ptr_array_new();
+	for (i=0;i<mdb->num_catalog;i++) {
+		entry = g_ptr_array_index(mdb->catalog,i);
+		data = g_memdup(entry,sizeof(MdbCatalogEntry));
+		g_ptr_array_add(newmdb->catalog, data);
+	}
+	mdb->backend_name = NULL;
+	if (mdb->f) {
+		mdb->f->refs++;
+	}
+	mdb_iconv_init(mdb);
+
+	return newmdb;
+}
+
+/* 
+** mdb_read a wrapper for read that bails if anything is wrong 
+*/
+ssize_t mdb_read_pg(MdbHandle *mdb, unsigned long pg)
+{
+	ssize_t len;
+
+	if (pg && mdb->cur_pg == pg) return mdb->fmt->pg_size;
+
+	len = _mdb_read_pg(mdb, mdb->pg_buf, pg);
+	//fprintf(stderr, "read page %d type %02x\n", pg, mdb->pg_buf[0]);
+	mdb->cur_pg = pg;
+	/* kan - reset the cur_pos on a new page read */
+	mdb->cur_pos = 0; /* kan */
+	return len;
+}
+ssize_t mdb_read_alt_pg(MdbHandle *mdb, unsigned long pg)
+{
+	ssize_t len;
+
+	len = _mdb_read_pg(mdb, mdb->alt_pg_buf, pg);
+	return len;
+}
+static ssize_t _mdb_read_pg(MdbHandle *mdb, unsigned char *pg_buf, unsigned long pg)
+{
+	ssize_t len;
+	struct stat status;
+	off_t offset = pg * mdb->fmt->pg_size;
+
+        fstat(mdb->f->fd, &status);
+        if (status.st_size < offset) { 
+                fprintf(stderr,"offset %lu is beyond EOF\n",offset);
+                return 0;
+        }
+	if (mdb->stats && mdb->stats->collect) 
+		mdb->stats->pg_reads++;
+
+	lseek(mdb->f->fd, offset, SEEK_SET);
+	len = read(mdb->f->fd,pg_buf,mdb->fmt->pg_size);
+	if (len==-1) {
+		perror("read");
+		return 0;
+	}
+	else if (len<mdb->fmt->pg_size) {
+		/* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->fmt->pg_size); */
+		return 0;
+	} 
+	return len;
+}
+void mdb_swap_pgbuf(MdbHandle *mdb)
+{
+char tmpbuf[MDB_PGSIZE];
+
+	memcpy(tmpbuf,mdb->pg_buf, MDB_PGSIZE);
+	memcpy(mdb->pg_buf,mdb->alt_pg_buf, MDB_PGSIZE);
+	memcpy(mdb->alt_pg_buf,tmpbuf,MDB_PGSIZE);
+}
+
+
+/* really stupid, just here for consistancy */
+unsigned char mdb_get_byte(unsigned char *buf, int offset)
+{
+	return buf[offset];
+}
+unsigned char mdb_pg_get_byte(MdbHandle *mdb, int offset)
+{
+	if (offset < 0 || offset+1 > mdb->fmt->pg_size) return -1;
+	mdb->cur_pos++;
+	return mdb->pg_buf[offset];
+}
+
+int mdb_get_int16(unsigned char *buf, int offset)
+{
+	return buf[offset+1]*256+buf[offset];
+}
+int mdb_pg_get_int16(MdbHandle *mdb, int offset)
+{
+	if (offset < 0 || offset+2 > mdb->fmt->pg_size) return -1;
+	mdb->cur_pos+=2;
+	return mdb_get_int16(mdb->pg_buf, offset);
+}
+
+gint32 mdb_pg_get_int24_msb(MdbHandle *mdb, int offset)
+{
+	gint32 l = 0;
+	if (offset <0 || offset+3 > mdb->fmt->pg_size) return -1;
+	mdb->cur_pos+=3;
+	memcpy(&l, &(mdb->pg_buf[offset]), 3);
+	return GINT32_FROM_BE(l);
+}
+gint32 mdb_get_int24(unsigned char *buf, int offset)
+{
+	gint32 l = 0;
+	memcpy(&l, &buf[offset], 3);
+	return GINT32_FROM_LE(l);
+}
+gint32 mdb_pg_get_int24(MdbHandle *mdb, int offset)
+{
+	if (offset <0 || offset+3 > mdb->fmt->pg_size) return -1;
+	mdb->cur_pos+=3;
+	return mdb_get_int24(mdb->pg_buf, offset);
+}
+
+long mdb_get_int32(unsigned char *buf, int offset)
+{
+	guint32 l;
+	memcpy(&l, &buf[offset], 4);
+	return (long)GINT32_FROM_LE(l);
+}
+long mdb_pg_get_int32(MdbHandle *mdb, int offset)
+{
+	if (offset <0 || offset+4 > mdb->fmt->pg_size) return -1;
+	mdb->cur_pos+=4;
+	return mdb_get_int32(mdb->pg_buf, offset);
+}
+
+float mdb_get_single(unsigned char *buf, int offset)
+{
+	union {guint32 g; float f;} f;
+	memcpy(&f, &buf[offset], 4);
+	f.g = GUINT32_FROM_LE(f.g);
+	return f.f;
+}
+float mdb_pg_get_single(MdbHandle *mdb, int offset)
+{
+       if (offset <0 || offset+4 > mdb->fmt->pg_size) return -1;
+       mdb->cur_pos+=4;
+       return mdb_get_single(mdb->pg_buf, offset);
+}
+
+double mdb_get_double(unsigned char *buf, int offset)
+{
+	union {guint64 g; double d;} d;
+	memcpy(&d, &buf[offset], 8);
+	d.g = GUINT64_FROM_LE(d.g);
+	return d.d;
+}
+double mdb_pg_get_double(MdbHandle *mdb, int offset)
+{
+	if (offset <0 || offset+8 > mdb->fmt->pg_size) return -1;
+	mdb->cur_pos+=8;
+	return mdb_get_double(mdb->pg_buf, offset);
+}
+
+
+int 
+mdb_set_pos(MdbHandle *mdb, int pos)
+{
+	if (pos<0 || pos >= mdb->fmt->pg_size) return 0;
+
+	mdb->cur_pos=pos;
+	return pos;
+}
+int mdb_get_pos(MdbHandle *mdb)
+{
+	return mdb->cur_pos;
+}
diff --git a/providers/mdb/libmdb-src/iconv.c b/providers/mdb/libmdb-src/iconv.c
new file mode 100644
index 0000000..df2abda
--- /dev/null
+++ b/providers/mdb/libmdb-src/iconv.c
@@ -0,0 +1,214 @@
+/* MDB Tools - A library for reading MS Access database files
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "mdbtools.h"
+#include "errno.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+/*
+ * This function is used in reading text data from an MDB table.
+ */
+int
+mdb_unicode2ascii(MdbHandle *mdb, unsigned char *src, unsigned int slen, unsigned char *dest, unsigned int dlen)
+{
+	unsigned char *tmp = NULL;
+	size_t tlen = 0;
+	size_t len_in, len_out;
+	char *in_ptr, *out_ptr;
+
+	if ((!src) || (!dest))
+		return 0;
+
+	/* Uncompress 'Unicode Compressed' string into tmp */
+	if (IS_JET4(mdb) && (slen>=2) && (src[0]==0xff) && (src[1]==0xfe)) {
+		unsigned int compress=1;
+		src += 2;
+		slen -= 2;
+		tmp = (unsigned char *)g_malloc(slen*2);
+		while (slen) {
+			if (*src == 0) {
+				compress = (compress) ? 0 : 1;
+				src++;
+				slen--;
+			} else if (compress) {
+				tmp[tlen++] = *src++;
+				tmp[tlen++] = 0;
+				slen--;
+			} else if (slen >= 2){
+				tmp[tlen++] = *src++;
+				tmp[tlen++] = *src++;
+				slen-=2;
+			}
+		}
+	}
+
+	in_ptr = (char *)((tmp) ? tmp : src);
+	out_ptr = (char *)dest;
+	len_in = (tmp) ? tlen : slen;
+	len_out = dlen;
+
+#if HAVE_ICONV
+	//printf("1 len_in %d len_out %d\n",len_in, len_out);
+	while (1) {
+		iconv(mdb->iconv_in, &in_ptr, &len_in, &out_ptr, &len_out);
+		if ((!len_in) || (errno == E2BIG)) break;
+		/* Don't bail if impossible conversion is encountered */
+		in_ptr += (IS_JET4(mdb)) ? 2 : 1;
+		len_in -= (IS_JET4(mdb)) ? 2 : 1;
+		*out_ptr++ = '?';
+		len_out--;
+	}
+	//printf("2 len_in %d len_out %d\n",len_in, len_out);
+	dlen -= len_out;
+#else
+	if (IS_JET3(mdb)) {
+		strncpy(out_ptr, in_ptr, len_in);
+		dlen = len_in;
+	} else {
+		/* rough UCS-2LE to ISO-8859-1 conversion */
+		unsigned int i;
+		for (i=0; i<len_in; i+=2)
+			dest[i/2] = (in_ptr[i+1] == 0) ? in_ptr[i] : '?';
+		dlen = len_in/2;
+	}
+#endif
+
+	if (tmp) g_free(tmp);
+	dest[dlen]='\0';
+	//printf("dest %s\n",dest);
+	return dlen;
+}
+
+/*
+ * This function is used in writing text data to an MDB table.
+ * If slen is 0, strlen will be used to calculate src's length.
+ */
+int
+mdb_ascii2unicode(MdbHandle *mdb, unsigned char *src, unsigned int slen, unsigned char *dest, unsigned int dlen)
+{
+        size_t len_in, len_out;
+        char *in_ptr, *out_ptr;
+
+	if ((!src) || (!dest))
+		return 0;
+
+        in_ptr = (char *)src;
+        out_ptr = (char *)dest;
+        len_in = (slen) ? slen : strlen(in_ptr);
+        len_out = dlen;
+
+#ifdef HAVE_ICONV
+	iconv(mdb->iconv_out, &in_ptr, &len_in, &out_ptr, &len_out);
+	//printf("len_in %d len_out %d\n", len_in, len_out);
+	dlen -= len_out;
+#else
+	if (IS_JET3(mdb)) {
+		dlen = MIN(len_in, len_out);
+		strncpy(out_ptr, in_ptr, dlen);
+	} else {
+		unsigned int i;
+		slen = MIN(len_in, len_out/2);
+		dlen = slen*2;
+		for (i=0; i<slen; i++) {
+			out_ptr[i*2] = in_ptr[i];
+			out_ptr[i*2+1] = 0;
+		}
+	}
+#endif
+
+	/* Unicode Compression */
+	if(IS_JET4(mdb) && (dlen>4)) {
+		unsigned char *tmp = g_malloc(dlen);
+		unsigned int tptr = 0, dptr = 0;
+		int comp = 1;
+
+		tmp[tptr++] = 0xff;
+		tmp[tptr++] = 0xfe;
+		while((dptr < dlen) && (tptr < dlen)) {
+			if (((dest[dptr+1]==0) && (comp==0))
+			 || ((dest[dptr+1]!=0) && (comp==1))) {
+				/* switch encoding mode */
+				tmp[tptr++] = 0;
+				comp = (comp) ? 0 : 1;
+			} else if (dest[dptr]==0) {
+				/* this string cannot be compressed */
+				tptr = dlen;
+			} else if (comp==1) {
+				/* encode compressed character */
+				tmp[tptr++] = dest[dptr];
+				dptr += 2;
+			} else if (tptr+1 < dlen) {
+				/* encode uncompressed character */
+				tmp[tptr++] = dest[dptr];
+				tmp[tptr++] = dest[dptr+1];
+				dptr += 2;
+			} else {
+				/* could not encode uncompressed character
+				 * into single byte */
+				tptr = dlen;
+			}
+		}
+		if (tptr < dlen) {
+			memcpy(dest, tmp, tptr);
+			dlen = tptr;
+		}
+		g_free(tmp);
+	}
+
+	return dlen;
+}
+
+void mdb_iconv_init(MdbHandle *mdb)
+{
+	char *iconv_code;
+
+	/* check environment variable */
+	if (!(iconv_code=(char *)getenv("MDB_ICONV"))) {
+		iconv_code="UTF-8";
+	}
+
+#ifdef HAVE_ICONV
+        if (IS_JET4(mdb)) {
+                mdb->iconv_out = iconv_open("UCS-2LE", iconv_code);
+                mdb->iconv_in = iconv_open(iconv_code, "UCS-2LE");
+        } else {
+                /* According to Microsoft Knowledge Base pages 289525 and */
+		/* 202427, code page info is not contained in the database */
+		char *jet3_iconv_code;
+
+		/* check environment variable */
+		if (!(jet3_iconv_code=(char *)getenv("MDB_JET3_CHARSET"))) {
+			jet3_iconv_code="CP1252";
+		}
+
+                mdb->iconv_out = iconv_open(jet3_iconv_code, iconv_code);
+                mdb->iconv_in = iconv_open(iconv_code, jet3_iconv_code);
+        }
+#endif
+}
+void mdb_iconv_close(MdbHandle *mdb)
+{
+#ifdef HAVE_ICONV
+        if (mdb->iconv_out != (iconv_t)-1) iconv_close(mdb->iconv_out);
+        if (mdb->iconv_in != (iconv_t)-1) iconv_close(mdb->iconv_in);
+#endif
+}
diff --git a/providers/mdb/libmdb-src/include/mdbodbc.h b/providers/mdb/libmdb-src/include/mdbodbc.h
new file mode 100644
index 0000000..9240ccf
--- /dev/null
+++ b/providers/mdb/libmdb-src/include/mdbodbc.h
@@ -0,0 +1,70 @@
+/* MDB Tools - A library for reading MS Access database file
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef _mdbodbc_h_
+#define _mdbodbc_h_
+
+#include <mdbtools.h>
+#include <mdbsql.h>
+
+#include <sql.h>
+#include <sqlext.h>
+#if defined(UNIXODBC)
+# include <odbcinst.h>
+#elif defined(IODBC)
+# include <iodbcinst.h>
+#endif
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+static char  rcsid_sql_h [ ] =
+         "$Id: mdbodbc.h,v 1.5 2005/01/15 05:02:13 calvinrsmith Exp $";
+static void *no_unused_sql_h_warn[]={rcsid_sql_h, no_unused_sql_h_warn};
+
+struct _henv {
+	MdbSQL *sql;	
+};
+struct _hdbc {
+	struct _henv *henv;
+};
+struct _hstmt {
+	struct _hdbc *hdbc;
+	/* reminder to self: the following is here for testing purposes.
+	 * please make dynamic before checking in 
+	 */
+	char query[4096];
+	struct _sql_bind_info *bind_head;
+	int rows_affected;
+};
+
+struct _sql_bind_info {
+	int column_number;
+	int column_bindtype;
+	int column_bindlen;
+	int *column_lenbind;
+	char *varaddr;
+	struct _sql_bind_info *next;
+};
+
+#ifdef __cplusplus
+}
+#endif
+#endif
diff --git a/providers/mdb/libmdb-src/include/mdbprivate.h b/providers/mdb/libmdb-src/include/mdbprivate.h
new file mode 100644
index 0000000..2570705
--- /dev/null
+++ b/providers/mdb/libmdb-src/include/mdbprivate.h
@@ -0,0 +1,35 @@
+/* MDB Tools - A library for reading MS Access database files
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _mdbprivate_h_
+#define _mdbprivate_h_
+
+/*
+ * This header is for stuff lacking a MDB_ or mdb_ something, so they won't be
+ * exported to calling programs.
+ */
+
+#include <config.h>
+
+#define _(String) (String)
+#define N_(String) String
+#define textdomain(Domain)
+#define bindtextdomain(Package, Directory)
+
+#endif
diff --git a/providers/mdb/libmdb-src/include/mdbsql.h b/providers/mdb/libmdb-src/include/mdbsql.h
new file mode 100644
index 0000000..de0accb
--- /dev/null
+++ b/providers/mdb/libmdb-src/include/mdbsql.h
@@ -0,0 +1,86 @@
+#ifndef _mdbsql_h_
+#define _mdbsql_h_
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <glib.h>
+#include <mdbtools.h>
+
+typedef struct {
+	MdbHandle *mdb;
+	int all_columns;
+	unsigned int num_columns;
+	GPtrArray *columns;
+	unsigned int num_tables;
+	GPtrArray *tables;
+	MdbTableDef *cur_table;
+	MdbSargNode *sarg_tree;
+	GList *sarg_stack;
+	/* FIX ME */
+	char *bound_values[256];
+	unsigned char *kludge_ttable_pg;
+	long max_rows;
+} MdbSQL;
+
+typedef struct {
+	char *name;
+	int  disp_size;
+	void *bind_addr;   /* if !NULL then cp parameter to here */
+	int  bind_type;
+	int  *bind_len;
+	int  bind_max;
+} MdbSQLColumn;
+
+typedef struct {
+	char *name;
+	char *alias;
+} MdbSQLTable;
+
+typedef struct {
+	char *col_name;
+	MdbSarg *sarg;
+} MdbSQLSarg;
+
+extern char *g_input_ptr;
+
+#undef YY_INPUT
+#define YY_INPUT(b, r, ms) (r = mdb_sql_yyinput(b, ms));
+
+extern MdbSQL *_mdb_sql(MdbSQL *sql);
+extern MdbSQL *mdb_sql_init();
+extern MdbSQLSarg *mdb_sql_alloc_sarg();
+extern MdbHandle *mdb_sql_open(MdbSQL *sql, char *db_name);
+extern int mdb_sql_add_sarg(MdbSQL *sql, char *col_name, int op, char *constant);
+extern void mdb_sql_all_columns(MdbSQL *sql);
+extern int mdb_sql_add_column(MdbSQL *sql, char *column_name);
+extern int mdb_sql_add_table(MdbSQL *sql, char *table_name);
+extern void mdb_sql_dump(MdbSQL *sql);
+extern void mdb_sql_exit(MdbSQL *sql);
+extern void mdb_sql_reset(MdbSQL *sql);
+extern void mdb_sql_listtables(MdbSQL *sql);
+extern void mdb_sql_select(MdbSQL *sql);
+extern void mdbsql_bind_all(MdbSQL *sql);
+extern void mdb_sql_dump_node(MdbSargNode *node, int level);
+extern void mdb_sql_close(MdbSQL *sql);
+extern void mdb_sql_add_or(MdbSQL *sql);
+extern void mdb_sql_add_and(MdbSQL *sql);
+extern void mdb_sql_listtables(MdbSQL *sql);
+extern void mdb_sql_add_not(MdbSQL *sql);
+extern void mdb_sql_describe_table(MdbSQL *sql);
+extern MdbSQL* mdb_sql_run_query (MdbSQL*, const gchar*);
+extern void mdb_sql_set_maxrow(MdbSQL *sql, int maxrow);
+extern int mdb_sql_eval_expr(MdbSQL *sql, char *const1, int op, char *const2);
+extern void mdb_sql_bind_all(MdbSQL *sql);
+extern int mdb_sql_fetch_row(MdbSQL *sql, MdbTableDef *table);
+extern int mdb_sql_add_temp_col(MdbSQL *sql, MdbTableDef *ttable, int col_num, char *name, int col_type, int col_size, int is_fixed);
+extern void mdb_sql_bind_column(MdbSQL *sql, int colnum, char *varaddr, int *len_ptr);
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/providers/mdb/libmdb-src/include/mdbtools.h b/providers/mdb/libmdb-src/include/mdbtools.h
new file mode 100644
index 0000000..dc43e5b
--- /dev/null
+++ b/providers/mdb/libmdb-src/include/mdbtools.h
@@ -0,0 +1,542 @@
+/* MDB Tools - A library for reading MS Access database files
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifndef _mdbtools_h_
+#define _mdbtools_h_
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <glib.h>
+
+#ifdef HAVE_ICONV
+#include <iconv.h>
+#endif
+
+#define MDB_DEBUG 0
+
+#define MDB_PGSIZE 4096
+#define MDB_MAX_OBJ_NAME 256
+#define MDB_MAX_COLS 256
+#define MDB_MAX_IDX_COLS 10
+#define MDB_CATALOG_PG 18
+#define MDB_MEMO_OVERHEAD 12
+#define MDB_BIND_SIZE 16384
+
+enum {
+	MDB_PAGE_DB = 0,
+	MDB_PAGE_DATA,
+	MDB_PAGE_TABLE,
+	MDB_PAGE_INDEX,
+	MDB_PAGE_LEAF,
+	MDB_PAGE_MAP
+};
+enum {
+	MDB_VER_JET3 = 0,
+	MDB_VER_JET4 = 1
+};
+enum {
+	MDB_FORM = 0,
+	MDB_TABLE,
+	MDB_MACRO,
+	MDB_SYSTEM_TABLE,
+	MDB_REPORT,
+	MDB_QUERY,
+	MDB_LINKED_TABLE,
+	MDB_MODULE,
+	MDB_RELATIONSHIP,
+	MDB_UNKNOWN_09,
+	MDB_UNKNOWN_0A,
+	MDB_DATABASE_PROPERTY,
+	MDB_ANY = -1
+};
+enum {
+	MDB_BOOL = 0x01,
+	MDB_BYTE = 0x02,
+	MDB_INT = 0x03,
+	MDB_LONGINT = 0x04,
+	MDB_MONEY = 0x05,
+	MDB_FLOAT = 0x06,
+	MDB_DOUBLE = 0x07,
+	MDB_SDATETIME = 0x08,
+	MDB_TEXT = 0x0a,
+	MDB_OLE = 0x0b,
+	MDB_MEMO = 0x0c,
+	MDB_REPID = 0x0f,
+	MDB_NUMERIC = 0x10
+};
+
+/* SARG operators */
+enum {
+	MDB_OR = 1,
+	MDB_AND,
+	MDB_NOT,
+	MDB_EQUAL,
+	MDB_GT,
+	MDB_LT,
+	MDB_GTEQ,
+	MDB_LTEQ,
+	MDB_LIKE,
+	MDB_ISNULL,
+	MDB_NOTNULL
+};
+
+typedef enum {
+	MDB_TABLE_SCAN,
+	MDB_LEAF_SCAN,
+	MDB_INDEX_SCAN
+} MdbStrategy;
+
+typedef enum {
+	MDB_NOFLAGS = 0x00,
+	MDB_WRITABLE = 0x01
+} MdbFileFlags;
+
+enum {
+	MDB_DEBUG_LIKE = 0x0001,
+	MDB_DEBUG_WRITE = 0x0002,
+	MDB_DEBUG_USAGE = 0x0004,
+	MDB_DEBUG_OLE = 0x0008,
+	MDB_DEBUG_ROW = 0x0010,
+	MDB_USE_INDEX = 0x0020,
+	MDB_NO_MEMO = 0x0040 /* don't follow memo fields */
+};
+
+#define mdb_is_logical_op(x) (x == MDB_OR || \
+				x == MDB_AND || \
+				x == MDB_NOT )
+
+#define mdb_is_relational_op(x) (x == MDB_EQUAL || \
+				x == MDB_GT || \
+				x == MDB_LT || \
+				x == MDB_GTEQ || \
+				x == MDB_LTEQ || \
+				x == MDB_LIKE || \
+				x == MDB_ISNULL || \
+				x == MDB_NOTNULL )
+
+enum {
+	MDB_ASC,
+	MDB_DESC
+};
+
+enum {
+	MDB_IDX_UNIQUE = 0x01,
+	MDB_IDX_IGNORENULLS = 0x02,
+	MDB_IDX_REQUIRED = 0x08 
+};
+
+#define IS_JET4(mdb) (mdb->f->jet_version==MDB_VER_JET4)
+#define IS_JET3(mdb) (mdb->f->jet_version==MDB_VER_JET3)
+
+/* hash to store registered backends */
+extern GHashTable	*mdb_backends;
+
+/* forward declarations */
+typedef struct mdbindex MdbIndex;
+typedef struct mdbsargtree MdbSargNode;
+
+typedef struct {
+	char *name;
+	unsigned char needs_length; /* or precision */
+	unsigned char needs_scale;
+	unsigned char needs_quotes;
+} MdbBackendType;
+		
+typedef struct {
+	 MdbBackendType *types_table;
+} MdbBackend;
+
+typedef struct {
+	gboolean collect;
+	unsigned long pg_reads;
+} MdbStatistics;
+
+typedef struct {
+	int           fd;
+	gboolean      writable;
+	char          *filename;
+	guint32		jet_version;
+	guint32		db_key;
+	char		db_passwd[14];
+	MdbBackend	*default_backend;
+	char			*backend_name;
+	MdbStatistics	*stats;
+	/* free map */
+	int  map_sz;
+	unsigned char *free_map;
+	/* reference count */
+	int refs;
+} MdbFile; 
+
+/* offset to row count on data pages...version dependant */
+typedef struct {
+	ssize_t		pg_size;
+	guint16		row_count_offset; 
+	guint16		tab_num_rows_offset;
+	guint16		tab_num_cols_offset;
+	guint16		tab_num_idxs_offset;
+	guint16		tab_num_ridxs_offset;
+	guint16		tab_usage_map_offset;
+	guint16		tab_first_dpg_offset;
+	guint16		tab_cols_start_offset;
+	guint16		tab_ridx_entry_size;
+	guint16		col_fixed_offset;
+	guint16		col_size_offset;
+	guint16		col_num_offset;
+	guint16		tab_col_entry_size;
+	guint16         tab_free_map_offset;
+	guint16		tab_col_offset_var;
+	guint16		tab_col_offset_fixed;
+	guint16		tab_row_col_num_offset;
+} MdbFormatConstants; 
+
+typedef struct {
+	MdbFile       *f;
+	guint32       cur_pg;
+	guint16       row_num;
+	unsigned int  cur_pos;
+	unsigned char pg_buf[MDB_PGSIZE];
+	unsigned char alt_pg_buf[MDB_PGSIZE];
+	unsigned int  num_catalog;
+	GPtrArray	*catalog;
+	MdbBackend	*default_backend;
+	char		*backend_name;
+	MdbFormatConstants *fmt;
+	MdbStatistics *stats;
+#ifdef HAVE_ICONV
+	iconv_t	iconv_in;
+	iconv_t	iconv_out;
+#endif
+} MdbHandle; 
+
+typedef struct {
+	MdbHandle	*mdb;
+	char           object_name[MDB_MAX_OBJ_NAME+1];
+	int            object_type;
+	unsigned long  table_pg; /* misnomer since object may not be a table */
+	unsigned long  kkd_pg;
+	unsigned int   kkd_rowid;
+	int			num_props;
+	GArray		*props;
+	GArray		*columns;
+	int		flags;
+} MdbCatalogEntry;
+
+typedef struct {
+	gchar *name;
+	GHashTable	*hash;
+} MdbProperties;
+
+typedef union {
+	int	i;
+	double	d;
+	char	s[256];
+} MdbAny;
+
+typedef struct {
+	char		name[MDB_MAX_OBJ_NAME+1];
+	int		col_type;
+	int		col_size;
+	void	*bind_ptr;
+	int		*len_ptr;
+	GHashTable	*properties;
+	unsigned int	num_sargs;
+	GPtrArray	*sargs;
+	GPtrArray	*idx_sarg_cache;
+	unsigned char   is_fixed;
+	int		query_order;
+	/* col_num is the current column order, 
+	 * does not include deletes */
+	int		col_num;	
+	int		cur_value_start;
+	int 		cur_value_len;
+	/* MEMO/OLE readers */
+	guint32		cur_blob_pg_row;
+	int		chunk_size;
+	/* numerics only */
+	int		col_prec;
+	int		col_scale;
+	MdbProperties	*props;
+	/* info needed for handling deleted/added columns */
+	int 		fixed_offset;
+	unsigned int	var_col_num;
+	/* row_col_num is the row column number order, 
+	 * including deleted columns */
+	int		row_col_num;
+} MdbColumn;
+
+struct mdbsargtree {
+	int       op;
+	MdbColumn *col;
+	MdbAny    value;
+	void      *parent;
+	MdbSargNode *left;
+	MdbSargNode *right;
+};
+
+typedef struct {
+	guint32 pg;
+	int start_pos;
+	int offset;
+	int len;
+	guint16 idx_starts[2000];	
+	unsigned char cache_value[256];
+} MdbIndexPage;
+
+typedef int (*MdbSargTreeFunc)(MdbSargNode *, gpointer data);
+
+#define MDB_MAX_INDEX_DEPTH 10
+
+typedef struct {
+	int cur_depth;
+	guint32 last_leaf_found;
+	int clean_up_mode;
+	MdbIndexPage pages[MDB_MAX_INDEX_DEPTH];
+} MdbIndexChain;
+
+typedef struct {
+	MdbCatalogEntry *entry;
+	char	name[MDB_MAX_OBJ_NAME+1];
+	unsigned int    num_cols;
+	GPtrArray	*columns;
+	unsigned int    num_rows;
+	int	index_start;
+	unsigned int    num_real_idxs;
+	unsigned int    num_idxs;
+	GPtrArray	*indices;
+	guint32	first_data_pg;
+	guint32	cur_pg_num;
+	guint32	cur_phys_pg;
+	unsigned int    cur_row;
+	int  noskip_del;  /* don't skip deleted rows */
+	/* object allocation map */
+	guint32  map_base_pg;
+	unsigned int  map_sz;
+	unsigned char *usage_map;
+	/* pages with free space left */
+	guint32  freemap_base_pg;
+	unsigned int  freemap_sz;
+	unsigned char *free_usage_map;
+	/* query planner */
+	MdbSargNode *sarg_tree;
+	MdbStrategy strategy;
+	MdbIndex *scan_idx;
+	MdbHandle *mdbidx;
+	MdbIndexChain *chain;
+	MdbProperties	*props;
+	unsigned int num_var_cols;  /* to know if row has variable columns */
+	/* temp table */
+	unsigned int  is_temp_table;
+	GPtrArray     *temp_table_pages;
+} MdbTableDef;
+
+struct mdbindex {
+	int		index_num;
+	char		name[MDB_MAX_OBJ_NAME+1];
+	unsigned char	index_type;
+	guint32		first_pg;
+	int		num_rows;  /* number rows in index */
+	unsigned int	num_keys;
+	short	key_col_num[MDB_MAX_IDX_COLS];
+	unsigned char	key_col_order[MDB_MAX_IDX_COLS];
+	unsigned char	flags;
+	MdbTableDef	*table;
+};
+
+typedef struct {
+	char		name[MDB_MAX_OBJ_NAME+1];
+} MdbColumnProp;
+
+typedef struct {
+	void *value;
+	int siz;
+	int start;
+	unsigned char is_null;
+	unsigned char is_fixed;
+	int colnum;
+	int offset;
+} MdbField;
+
+typedef struct {
+	int	op;
+	MdbAny	value;
+} MdbSarg;
+
+/* mem.c */
+extern void mdb_init();
+extern void mdb_exit();
+
+/* file.c */
+extern ssize_t mdb_read_pg(MdbHandle *mdb, unsigned long pg);
+extern ssize_t mdb_read_alt_pg(MdbHandle *mdb, unsigned long pg);
+extern unsigned char mdb_get_byte(unsigned char *buf, int offset);
+extern int    mdb_get_int16(unsigned char *buf, int offset);
+extern gint32   mdb_get_int24(unsigned char *buf, int offset);
+extern long   mdb_get_int32(unsigned char *buf, int offset);
+extern float  mdb_get_single(unsigned char *buf, int offset);
+extern double mdb_get_double(unsigned char *buf, int offset);
+extern unsigned char mdb_pg_get_byte(MdbHandle *mdb, int offset);
+extern int    mdb_pg_get_int16(MdbHandle *mdb, int offset);
+extern gint32   mdb_pg_get_int24(MdbHandle *mdb, int offset);
+extern long   mdb_pg_get_int32(MdbHandle *mdb, int offset);
+extern float  mdb_pg_get_single(MdbHandle *mdb, int offset);
+extern double mdb_pg_get_double(MdbHandle *mdb, int offset);
+extern gint32 mdb_pg_get_int24_msb(MdbHandle *mdb, int offset);
+extern MdbHandle *mdb_open(const char *filename, MdbFileFlags flags);
+extern void mdb_close(MdbHandle *mdb);
+extern MdbHandle *mdb_clone_handle(MdbHandle *mdb);
+extern void mdb_swap_pgbuf(MdbHandle *mdb);
+extern long _mdb_get_int32(unsigned char *buf, int offset);
+
+/* catalog.c */
+extern void mdb_free_catalog(MdbHandle *mdb);
+extern GPtrArray *mdb_read_catalog(MdbHandle *mdb, int obj_type);
+extern void mdb_dump_catalog(MdbHandle *mdb, int obj_type);
+extern char *mdb_get_objtype_string(int obj_type);
+
+/* table.c */
+extern MdbTableDef *mdb_alloc_tabledef(MdbCatalogEntry *entry);
+extern void mdb_free_tabledef(MdbTableDef *table);
+extern MdbTableDef *mdb_read_table(MdbCatalogEntry *entry);
+extern MdbTableDef *mdb_read_table_by_name(MdbHandle *mdb, gchar *table_name, int obj_type);
+extern void mdb_append_column(GPtrArray *columns, MdbColumn *in_col);
+extern void mdb_free_columns(GPtrArray *columns);
+extern GPtrArray *mdb_read_columns(MdbTableDef *table);
+extern void mdb_table_dump(MdbCatalogEntry *entry);
+extern guint16 read_pg_if_16(MdbHandle *mdb, int *cur_pos);
+extern guint32 read_pg_if_32(MdbHandle *mdb, int *cur_pos);
+extern int read_pg_if(MdbHandle *mdb, int *cur_pos, int offset);
+extern guint16 read_pg_if_n(MdbHandle *mdb, unsigned char *buf, int *cur_pos, int len);
+extern int mdb_is_user_table(MdbCatalogEntry *entry);
+extern int mdb_is_system_table(MdbCatalogEntry *entry);
+
+/* data.c */
+extern int mdb_bind_column_by_name(MdbTableDef *table, gchar *col_name, void *bind_ptr, int *len_ptr);
+extern void mdb_data_dump(MdbTableDef *table);
+extern void mdb_bind_column(MdbTableDef *table, int col_num, void *bind_ptr, int *len_ptr);
+extern int mdb_rewind_table(MdbTableDef *table);
+extern int mdb_fetch_row(MdbTableDef *table);
+extern int mdb_is_fixed_col(MdbColumn *col);
+extern char *mdb_col_to_string(MdbHandle *mdb, unsigned char *buf, int start, int datatype, int size);
+extern int mdb_find_pg_row(MdbHandle *mdb, int pg_row, void **buf, int *off, size_t *len);
+extern int mdb_find_row(MdbHandle *mdb, int row, int *start, int *len);
+extern int mdb_find_end_of_row(MdbHandle *mdb, int row);
+extern int mdb_col_fixed_size(MdbColumn *col);
+extern int mdb_col_disp_size(MdbColumn *col);
+extern size_t mdb_ole_read_next(MdbHandle *mdb, MdbColumn *col, void *ole_ptr);
+extern size_t mdb_ole_read(MdbHandle *mdb, MdbColumn *col, void *ole_ptr, int chunk_size);
+extern void mdb_set_date_fmt(const char *);
+extern int mdb_read_row(MdbTableDef *table, unsigned int row);
+
+/* dump.c */
+extern void buffer_dump(const unsigned char* buf, int start, int end);
+
+/* backend.c */
+extern char *mdb_get_coltype_string(MdbBackend *backend, int col_type);
+extern int  mdb_coltype_takes_length(MdbBackend *backend, int col_type);
+extern void mdb_init_backends();
+extern void mdb_register_backend(MdbBackendType *backend, char *backend_name);
+extern void mdb_remove_backends();
+extern int  mdb_set_default_backend(MdbHandle *mdb, char *backend_name);
+extern char *mdb_get_relationships(MdbHandle *mdb);
+
+/* sargs.c */
+extern int mdb_test_sargs(MdbTableDef *table, MdbField *fields, int num_fields);
+extern int mdb_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSargNode *node, MdbField *field);
+extern void mdb_sql_walk_tree(MdbSargNode *node, MdbSargTreeFunc func, gpointer data);
+extern int mdb_find_indexable_sargs(MdbSargNode *node, gpointer data);
+extern int mdb_add_sarg_by_name(MdbTableDef *table, char *colname, MdbSarg *in_sarg);
+extern int mdb_test_string(MdbSargNode *node, char *s);
+extern int mdb_test_int(MdbSargNode *node, gint32 i);
+extern int mdb_add_sarg(MdbColumn *col, MdbSarg *in_sarg);
+
+
+
+/* index.c */
+extern GPtrArray *mdb_read_indices(MdbTableDef *table);
+extern void mdb_index_dump(MdbTableDef *table, MdbIndex *idx);
+extern void mdb_index_scan_free(MdbTableDef *table);
+extern int mdb_index_find_next_on_page(MdbHandle *mdb, MdbIndexPage *ipg);
+extern int mdb_index_find_next(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 *pg, guint16 *row);
+extern void mdb_index_hash_text(guchar *text, guchar *hash);
+extern void mdb_index_scan_init(MdbHandle *mdb, MdbTableDef *table);
+extern int mdb_index_find_row(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 pg, guint16 row);
+extern void mdb_index_swap_n(unsigned char *src, int sz, unsigned char *dest);
+extern void mdb_free_indices(GPtrArray *indices);
+void mdb_index_page_reset(MdbIndexPage *ipg);
+extern int mdb_index_pack_bitmap(MdbHandle *mdb, MdbIndexPage *ipg);
+
+/* stats.c */
+extern void mdb_stats_on(MdbHandle *mdb);
+extern void mdb_stats_off(MdbHandle *mdb);
+extern void mdb_dump_stats(MdbHandle *mdb);
+
+/* like.c */
+extern int mdb_like_cmp(char *s, char *r);
+
+/* write.c */
+extern int mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields);
+extern guint16 mdb_add_row_to_pg(MdbTableDef *table, unsigned char *row_buffer, int new_row_size);
+extern int mdb_update_index(MdbTableDef *table, MdbIndex *idx, unsigned int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum);
+extern int mdb_pack_row(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields);
+extern int mdb_replace_row(MdbTableDef *table, int row, unsigned char *new_row, int new_row_size);
+extern int mdb_pg_get_freespace(MdbHandle *mdb);
+extern int mdb_update_row(MdbTableDef *table);
+extern unsigned char *mdb_new_data_pg(MdbCatalogEntry *entry);
+
+/* map.c */
+extern guint32 mdb_map_find_next_freepage(MdbTableDef *table, int row_size);
+extern guint32 mdb_map_find_next(MdbHandle *mdb, unsigned char *map, unsigned int map_sz, guint32 start_pg);
+
+/* props.c */
+extern GPtrArray *mdb_read_props_list(gchar *kkd, int len);
+extern void mdb_free_props(MdbProperties *props);
+extern MdbProperties *mdb_read_props(MdbHandle *mdb, GPtrArray *names, gchar *kkd, int len);
+
+/* worktable.c */
+extern MdbTableDef *mdb_create_temp_table(MdbHandle *mdb, char *name);
+extern void mdb_temp_table_add_col(MdbTableDef *table, MdbColumn *col);
+extern void mdb_fill_temp_col(MdbColumn *tcol, char *col_name, int col_size, int col_type, int is_fixed);
+extern void mdb_fill_temp_field(MdbField *field, void *value, int siz, int is_fixed, int is_null, int start, int column);
+extern void mdb_temp_columns_end(MdbTableDef *table);
+
+/* options.c */
+extern int mdb_get_option(unsigned long optnum);
+extern void mdb_debug(int klass, char *fmt, ...);
+
+/* iconv.c */
+extern int mdb_unicode2ascii(MdbHandle *mdb, unsigned char *src, unsigned int slen, unsigned char *dest, unsigned int dlen);
+extern int mdb_ascii2unicode(MdbHandle *mdb, unsigned char *src, unsigned int slen, unsigned char *dest, unsigned int dlen);
+extern void mdb_iconv_init(MdbHandle *mdb);
+extern void mdb_iconv_close(MdbHandle *mdb);
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif /* _mdbtools_h_ */
diff --git a/providers/mdb/libmdb-src/include/mdbver.h b/providers/mdb/libmdb-src/include/mdbver.h
new file mode 100644
index 0000000..15243fd
--- /dev/null
+++ b/providers/mdb/libmdb-src/include/mdbver.h
@@ -0,0 +1,26 @@
+/* MDB Tools - A library for reading MS Access database files
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef _mdbver_h_
+#define _mdbver_h_
+
+#define MDB_FULL_VERSION "mdbtools v0.6pre1"
+#define MDB_VERSION_NO "0.6pre1"
+
+#endif
diff --git a/providers/mdb/libmdb-src/index.c b/providers/mdb/libmdb-src/index.c
new file mode 100644
index 0000000..6c2d031
--- /dev/null
+++ b/providers/mdb/libmdb-src/index.c
@@ -0,0 +1,886 @@
+/* MDB Tools - A library for reading MS Access database file
+ * Copyright (C) 2000-2004 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "mdbtools.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+MdbIndexPage *mdb_index_read_bottom_pg(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain);
+MdbIndexPage *mdb_chain_add_page(MdbHandle *mdb, MdbIndexChain *chain, guint32 pg);
+
+char idx_to_text[] = {
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0-7     0x00-0x07 */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 8-15    0x09-0x0f */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 16-23   0x10-0x17 */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 24-31   0x19-0x1f */
+' ',  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 32-39   0x20-0x27 */
+0x00, 0x00, 0x00, 0x00, 0x00, ' ',  ' ',  0x00, /* 40-47   0x29-0x2f */
+'V',  'W',  'X',  'Y',  'Z',  '[',  '\\', ']',  /* 48-55   0x30-0x37 */
+'^',  '_',  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 56-63   0x39-0x3f */
+0x00, '`',  'a',  'b',  'd',  'f',  'g',  'h',  /* 64-71   0x40-0x47 */
+'i',  'j',  'k',  'l',  'm',  'o',  'p',  'r',  /* 72-79   0x49-0x4f  H */
+'s',  't',  'u',  'v',  'w',  'x',  'z',  '{',  /* 80-87   0x50-0x57  P */
+'|',  '}',  '~',  '5',  '6',  '7',  '8',  '9',  /* 88-95   0x59-0x5f */
+0x00, '`',  'a',  'b',  'd',  'f',  'g',  'h',  /* 96-103  0x60-0x67 */
+'i',  'j',  'k',  'l',  'm',  'o',  'p',  'r',  /* 014-111 0x69-0x6f  h */
+'s',  't',  'u',  'v',  'w',  'x',  'z',  '{',  /* 112-119 0x70-0x77  p */
+'|',  '}',  '~',  0x00, 0x00, 0x00, 0x00, 0x00, /* 120-127 0x78-0x7f */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 128-135 0x80-0x87 */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x88-0x8f */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x90-0x97 */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0x98-0x9f */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa0-0xa7 */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xa8-0xaf */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb0-0xb7 */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xb8-0xbf */
+0x00, 0x00, 0x00, 0x00, 0x00, '`',  0x00, 0x00, /* 0xc0-0xc7 */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xc8-0xcf */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd0-0xd7 */
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xd8-0xdf */
+0x00, '`',  0x00, '`',  '`',  '`',  0x00, 0x00, /* 0xe0-0xe7 */
+'f',  'f',  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 0xe8-0xef */
+0x00, 0x00, 0x00, 'r',  0x00, 0x00, 'r',  0x00, /* 0xf0-0xf7 */
+0x81, 0x00, 0x00, 0x00, 'x',  0x00, 0x00, 0x00, /* 0xf8-0xff */
+};
+
+
+GPtrArray *
+mdb_read_indices(MdbTableDef *table)
+{
+	MdbCatalogEntry *entry = table->entry;
+	MdbHandle *mdb = entry->mdb;
+	MdbFormatConstants *fmt = mdb->fmt;
+	MdbIndex *pidx;
+	unsigned int i, j;
+	int idx_num, key_num, col_num;
+	int cur_pos, name_sz, idx2_sz, type_offset;
+	int index_start_pg = mdb->cur_pg;
+	gchar *tmpbuf;
+
+        table->indices = g_ptr_array_new();
+
+        if (IS_JET4(mdb)) {
+		cur_pos = table->index_start + 52 * table->num_real_idxs;
+		idx2_sz = 28;
+		type_offset = 23;
+	} else {
+		cur_pos = table->index_start + 39 * table->num_real_idxs;
+		idx2_sz = 20;
+		type_offset = 19;
+	}
+
+	tmpbuf = (gchar *) g_malloc(idx2_sz);
+	for (i=0;i<table->num_idxs;i++) {
+		read_pg_if_n(mdb, tmpbuf, &cur_pos, idx2_sz);
+		cur_pos += idx2_sz;
+		pidx = (MdbIndex *) g_malloc0(sizeof(MdbIndex));
+		pidx->table = table;
+		pidx->index_num = mdb_get_int16(tmpbuf, 4);
+		pidx->index_type = tmpbuf[type_offset]; 
+		g_ptr_array_add(table->indices, pidx);
+	}
+	g_free(tmpbuf);
+
+	for (i=0;i<table->num_idxs;i++) {
+		pidx = g_ptr_array_index (table->indices, i);
+		if (IS_JET4(mdb)) {
+			name_sz=read_pg_if_16(mdb, &cur_pos);
+			cur_pos += 2;
+		} else {
+			read_pg_if(mdb, &cur_pos, 0);
+			name_sz=mdb->pg_buf[cur_pos++];
+		}
+		tmpbuf = g_malloc(name_sz);
+		read_pg_if_n(mdb, tmpbuf, &cur_pos, name_sz);
+		cur_pos += name_sz;
+		mdb_unicode2ascii(mdb, tmpbuf, name_sz, pidx->name, MDB_MAX_OBJ_NAME); 
+		g_free(tmpbuf);
+		//fprintf(stderr, "index name %s\n", pidx->name);
+	}
+
+	mdb_read_alt_pg(mdb, entry->table_pg);
+	mdb_read_pg(mdb, index_start_pg);
+	cur_pos = table->index_start;
+	idx_num=0;
+	for (i=0;i<table->num_real_idxs;i++) {
+		if (IS_JET4(mdb)) cur_pos += 4;
+		do {
+			pidx = g_ptr_array_index (table->indices, idx_num++);
+		} while (pidx && pidx->index_type==2);
+
+		/* if there are more real indexes than index entries left after
+		   removing type 2's decrement real indexes and continue.  Happens
+		   on Northwind Orders table.
+		*/
+		if (!pidx) {
+			table->num_real_idxs--;
+			continue;
+		}
+
+		pidx->num_rows = mdb_get_int32(mdb->alt_pg_buf, 
+				fmt->tab_cols_start_offset +
+				(i*fmt->tab_ridx_entry_size));
+
+		key_num=0;
+		for (j=0;j<MDB_MAX_IDX_COLS;j++) {
+			col_num=read_pg_if_16(mdb,&cur_pos);
+			cur_pos += 2;
+			read_pg_if(mdb, &cur_pos, 0);
+			cur_pos++;
+			if (col_num == 0xFFFF)
+				continue;
+			/* set column number to a 1 based column number and store */
+			pidx->key_col_num[key_num] = col_num + 1;
+			pidx->key_col_order[key_num] =
+				(mdb->pg_buf[cur_pos-1]) ? MDB_ASC : MDB_DESC;
+			key_num++;
+		}
+		pidx->num_keys = key_num;
+
+		cur_pos += 4;
+		pidx->first_pg = read_pg_if_32(mdb, &cur_pos);
+		cur_pos += 4;
+		read_pg_if(mdb, &cur_pos, 0);
+		pidx->flags = mdb->pg_buf[cur_pos++];
+		if (IS_JET4(mdb)) cur_pos += 9;
+	}
+	return NULL;
+}
+void
+mdb_index_hash_text(guchar *text, guchar *hash)
+{
+	unsigned int k;
+
+	for (k=0;k<strlen(text);k++) {
+		hash[k] = idx_to_text[text[k]];
+		if (!(hash[k])) fprintf(stderr, 
+				"No translation available for %02x %d\n", 
+				text[k],text[k]);
+	}
+	hash[strlen(text)]=0;
+}
+/*
+ * reverse the order of the column for hashing
+ */
+void
+mdb_index_swap_n(unsigned char *src, int sz, unsigned char *dest)
+{
+	int i, j = 0;
+
+	for (i = sz-1; i >= 0; i--) {
+		dest[j++] = src[i];
+	}
+}
+void 
+mdb_index_cache_sarg(MdbColumn *col, MdbSarg *sarg, MdbSarg *idx_sarg)
+{
+	//guint32 cache_int;
+	unsigned char *c;
+
+	switch (col->col_type) {
+		case MDB_TEXT:
+		mdb_index_hash_text(sarg->value.s, idx_sarg->value.s);
+		break;
+
+		case MDB_LONGINT:
+		idx_sarg->value.i = GUINT32_SWAP_LE_BE(sarg->value.i);
+		//cache_int = sarg->value.i * -1;
+		c = (unsigned char *) &(idx_sarg->value.i);
+		c[0] |= 0x80;
+		//printf("int %08x %02x %02x %02x %02x\n", sarg->value.i, c[0], c[1], c[2], c[3]);
+		break;	
+
+		case MDB_INT:
+		break;	
+
+		default:
+		break;	
+	}
+}
+#if 0
+int 
+mdb_index_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSarg *sarg, int offset, int len)
+{
+char tmpbuf[256];
+int lastchar;
+
+	switch (col->col_type) {
+		case MDB_BYTE:
+			return mdb_test_int(sarg, mdb_pg_get_byte(mdb, offset));
+			break;
+		case MDB_INT:
+			return mdb_test_int(sarg, mdb_pg_get_int16(mdb, offset));
+			break;
+		case MDB_LONGINT:
+			return mdb_test_int(sarg, mdb_pg_get_int32(mdb, offset));
+			break;
+		case MDB_TEXT:
+			strncpy(tmpbuf, &mdb->pg_buf[offset],255);
+			lastchar = len > 255 ? 255 : len;
+			tmpbuf[lastchar]='\0';
+			return mdb_test_string(sarg, tmpbuf);
+		default:
+			fprintf(stderr, "Calling mdb_test_sarg on unknown type.  Add code to mdb_test_sarg() for type %d\n",col->col_type);
+			break;
+	}
+	return 1;
+}
+#endif
+int
+mdb_index_test_sargs(MdbHandle *mdb, MdbIndex *idx, unsigned char *buf, int len)
+{
+	unsigned int i, j;
+	MdbColumn *col;
+	MdbTableDef *table = idx->table;
+	MdbSarg *idx_sarg;
+	MdbSarg *sarg;
+	MdbField field;
+	MdbSargNode node;
+	//int c_offset = 0, 
+	int c_len;
+
+	//fprintf(stderr,"mdb_index_test_sargs called on ");
+	//for (i=0;i<len;i++)
+		//fprintf(stderr,"%02x ",buf[i]); //mdb->pg_buf[offset+i]);
+	//fprintf(stderr,"\n");
+	for (i=0;i<idx->num_keys;i++) {
+		//c_offset++; /* the per column null indicator/flags */
+		col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1);
+		/*
+		 * This will go away eventually
+		 */
+		if (col->col_type==MDB_TEXT) {
+			//c_len = strlen(&mdb->pg_buf[offset + c_offset]);
+			c_len = strlen(buf);
+		} else {
+			c_len = col->col_size;
+			//fprintf(stderr,"Only text types currently supported.  How did we get here?\n");
+		}
+		/*
+		 * If we have no cached index values for this column, 
+		 * create them.
+		 */
+		if (col->num_sargs && !col->idx_sarg_cache) {
+			col->idx_sarg_cache = g_ptr_array_new();
+			for (j=0;j<col->num_sargs;j++) {
+				sarg = g_ptr_array_index (col->sargs, j);
+				idx_sarg = g_memdup(sarg,sizeof(MdbSarg));
+				//printf("calling mdb_index_cache_sarg\n");
+				mdb_index_cache_sarg(col, sarg, idx_sarg);
+				g_ptr_array_add(col->idx_sarg_cache, idx_sarg);
+			}
+		}
+
+		for (j=0;j<col->num_sargs;j++) {
+			sarg = g_ptr_array_index (col->idx_sarg_cache, j);
+			/* XXX - kludge */
+			node.op = sarg->op;
+			node.value = sarg->value;
+			//field.value = &mdb->pg_buf[offset + c_offset];
+			field.value = buf;
+		       	field.siz = c_len;
+		       	field.is_null = FALSE;
+			if (!mdb_test_sarg(mdb, col, &node, &field)) {
+				/* sarg didn't match, no sense going on */
+				return 0;
+			}
+		}
+	}
+	return 1;
+}
+/*
+ * pack the pages bitmap
+ */
+int
+mdb_index_pack_bitmap(MdbHandle *mdb, MdbIndexPage *ipg)
+{
+	int mask_bit = 0;
+	int mask_pos = 0x16;
+	int mask_byte = 0;
+	int elem = 0;
+	int len, start, i;
+
+	start = ipg->idx_starts[elem++];
+
+	while (start) {
+		//fprintf(stdout, "elem %d is %d\n", elem, ipg->idx_starts[elem]);
+		len = ipg->idx_starts[elem] - start;
+		//fprintf(stdout, "len is %d\n", len);
+		for (i=0; i < len; i++) {
+			mask_bit++;
+			if (mask_bit==8) {
+				mask_bit=0;
+				mdb->pg_buf[mask_pos++] = mask_byte;
+				mask_byte = 0;
+			}
+			/* upon reaching the len, set the bit */
+		}
+		mask_byte = (1 << mask_bit) | mask_byte;
+		//fprintf(stdout, "mask byte is %02x at %d\n", mask_byte, mask_pos);
+		start = ipg->idx_starts[elem++];
+	}
+	/* flush the last byte if any */
+	mdb->pg_buf[mask_pos++] = mask_byte;
+	/* remember to zero the rest of the bitmap */
+	for (i = mask_pos; i < 0xf8; i++) {
+		mdb->pg_buf[mask_pos++] = 0;
+	}
+	return 0;
+}
+/*
+ * unpack the pages bitmap
+ */
+int
+mdb_index_unpack_bitmap(MdbHandle *mdb, MdbIndexPage *ipg)
+{
+	int mask_bit = 0;
+	int mask_pos = 0x16;
+	int mask_byte;
+	int start = 0xf8;
+	int elem = 0;
+	int len = 0;
+
+	ipg->idx_starts[elem++]=start;
+
+	//fprintf(stdout, "Unpacking index page %lu\n", ipg->pg);
+	do {
+		len = 0;
+		do {
+			mask_bit++;
+			if (mask_bit==8) {
+				mask_bit=0;
+				mask_pos++;
+			}
+			mask_byte = mdb->pg_buf[mask_pos];
+			len++;
+		} while (mask_pos <= 0xf8 && !((1 << mask_bit) & mask_byte));
+		//fprintf(stdout, "%d %d %d %d\n", mask_pos, mask_bit, mask_byte, len);
+
+		start += len;
+		if (mask_pos < 0xf8) ipg->idx_starts[elem++]=start;
+
+	} while (mask_pos < 0xf8);
+
+	/* if we zero the next element, so we don't pick up the last pages starts*/
+	ipg->idx_starts[elem]=0;
+
+	return elem;
+}
+/*
+ * find the next entry on a page (either index or leaf). Uses state information
+ * stored in the MdbIndexPage across calls.
+ */
+int
+mdb_index_find_next_on_page(MdbHandle *mdb, MdbIndexPage *ipg)
+{
+	if (!ipg->pg) return 0;
+
+	/* if this page has not been unpacked to it */
+	if (!ipg->idx_starts[0]){
+		//fprintf(stdout, "Unpacking page %d\n", ipg->pg);
+		mdb_index_unpack_bitmap(mdb, ipg);
+	}
+
+	
+	if (ipg->idx_starts[ipg->start_pos + 1]==0) return 0; 
+	ipg->len = ipg->idx_starts[ipg->start_pos+1] - ipg->idx_starts[ipg->start_pos];
+	ipg->start_pos++;
+	//fprintf(stdout, "Start pos %d\n", ipg->start_pos);
+
+	return ipg->len;
+}
+void mdb_index_page_reset(MdbIndexPage *ipg)
+{
+	ipg->offset = 0xf8; /* start byte of the index entries */
+	ipg->start_pos=0;
+	ipg->len = 0; 
+	ipg->idx_starts[0]=0;
+}
+void mdb_index_page_init(MdbIndexPage *ipg)
+{
+	memset(ipg, 0, sizeof(MdbIndexPage));
+	mdb_index_page_reset(ipg);
+}
+/*
+ * find the next leaf page if any given a chain. Assumes any exhausted leaf 
+ * pages at the end of the chain have been peeled off before the call.
+ */
+MdbIndexPage *
+mdb_find_next_leaf(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain)
+{
+	MdbIndexPage *ipg, *newipg;
+	guint32 pg;
+	guint passed = 0;
+
+	ipg = mdb_index_read_bottom_pg(mdb, idx, chain);
+
+	/*
+	 * If we are at the first page deep and it's not an index page then
+	 * we are simply done. (there is no page to find
+	 */
+
+	if (mdb->pg_buf[0]==MDB_PAGE_LEAF) {
+		/* Indexes can have leaves at the end that don't appear
+		 * in the upper tree, stash the last index found so
+		 * we can follow it at the end.  */
+		chain->last_leaf_found = ipg->pg;
+		return ipg;
+	}
+
+	/*
+	 * apply sargs here, currently we don't
+	 */
+	do {
+		ipg->len = 0;
+		//printf("finding next on pg %lu\n", ipg->pg);
+		if (!mdb_index_find_next_on_page(mdb, ipg)) {
+			//printf("find_next_on_page returned 0\n");
+			return 0;
+		}
+		pg = mdb_pg_get_int24_msb(mdb, ipg->offset + ipg->len - 3);
+		//printf("Looking at pg %lu at %lu %d\n", pg, ipg->offset, ipg->len);
+		ipg->offset += ipg->len;
+
+		/*
+		 * add to the chain and call this function
+		 * recursively.
+		 */
+		newipg = mdb_chain_add_page(mdb, chain, pg);
+		newipg = mdb_find_next_leaf(mdb, idx, chain);
+		//printf("returning pg %lu\n",newipg->pg);
+		return newipg;
+	} while (!passed);
+	/* no more pages */
+	return NULL;
+
+}
+MdbIndexPage *
+mdb_chain_add_page(MdbHandle *mdb, MdbIndexChain *chain, guint32 pg)
+{
+	MdbIndexPage *ipg;
+
+	chain->cur_depth++;
+	if (chain->cur_depth > MDB_MAX_INDEX_DEPTH) {
+		fprintf(stderr,"Error! maximum index depth of %d exceeded.  This is probably due to a programming bug, If you are confident that your indexes really are this deep, adjust MDB_MAX_INDEX_DEPTH in mdbtools.h and recompile.\n", MDB_MAX_INDEX_DEPTH);
+		exit(1);
+	}
+	ipg = &(chain->pages[chain->cur_depth - 1]);
+	mdb_index_page_init(ipg);
+	ipg->pg = pg;
+
+	return ipg;
+}
+/*
+ * returns the bottom page of the IndexChain, if IndexChain is empty it 
+ * initializes it by reading idx->first_pg (the root page)
+ */
+MdbIndexPage *
+mdb_index_read_bottom_pg(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain)
+{
+	MdbIndexPage *ipg;
+
+	/*
+	 * if it's new use the root index page (idx->first_pg)
+	 */
+	if (!chain->cur_depth) {
+		ipg = &(chain->pages[0]);
+		mdb_index_page_init(ipg);
+		chain->cur_depth = 1;
+		ipg->pg = idx->first_pg;
+		if (!(ipg = mdb_find_next_leaf(mdb, idx, chain)))
+			return 0;
+	} else {
+		ipg = &(chain->pages[chain->cur_depth - 1]);
+		ipg->len = 0; 
+	}
+
+	mdb_read_pg(mdb, ipg->pg);
+
+	return ipg;
+}
+/*
+ * unwind the stack and search for new leaf node
+ */
+MdbIndexPage *
+mdb_index_unwind(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain)
+{
+	MdbIndexPage *ipg;
+
+	//printf("page %lu finished\n",ipg->pg);
+	if (chain->cur_depth==1) {
+		//printf("cur_depth == 1 we're out\n");
+		return NULL;
+	}
+	/* 
+	* unwind the stack until we find something or reach 
+	* the top.
+	*/
+	ipg = NULL;
+	while (chain->cur_depth>1 && ipg==NULL) {
+		//printf("chain depth %d\n", chain->cur_depth);
+		chain->cur_depth--;
+		ipg = mdb_find_next_leaf(mdb, idx, chain);
+		if (ipg) mdb_index_find_next_on_page(mdb, ipg);
+	}
+	if (chain->cur_depth==1) {
+		//printf("last leaf %lu\n", chain->last_leaf_found);
+		return NULL;
+	}
+	return ipg;
+}
+/*
+ * the main index function.
+ * caller provides an index chain which is the current traversal of index
+ * pages from the root page to the leaf.  Initially passed as blank, 
+ * mdb_index_find_next will store it's state information here. Each invocation
+ * then picks up where the last one left off, allowing us to scroll through
+ * the index one by one.
+ *
+ * Sargs are applied here but also need to be applied on the whole row b/c
+ * text columns may return false positives due to hashing and non-index
+ * columns with sarg values can't be tested here.
+ */
+int
+mdb_index_find_next(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 *pg, guint16 *row)
+{
+	MdbIndexPage *ipg;
+	int passed = 0;
+	int idx_sz;
+	int idx_start = 0;
+	MdbColumn *col;
+
+	ipg = mdb_index_read_bottom_pg(mdb, idx, chain);
+
+	/*
+	 * loop while the sargs don't match
+	 */
+	do {
+		ipg->len = 0;
+		/*
+		 * if no more rows on this leaf, try to find a new leaf
+		 */
+		if (!mdb_index_find_next_on_page(mdb, ipg)) {
+			if (!chain->clean_up_mode) {
+				if (!(ipg = mdb_index_unwind(mdb, idx, chain)))
+					chain->clean_up_mode = 1;
+			}
+			if (chain->clean_up_mode) {
+				//fprintf(stdout,"in cleanup mode\n");
+
+				if (!chain->last_leaf_found) return 0;
+				mdb_read_pg(mdb, chain->last_leaf_found);
+				chain->last_leaf_found = mdb_pg_get_int24(mdb, 0x0c);
+				//printf("next leaf %lu\n", chain->last_leaf_found);
+				mdb_read_pg(mdb, chain->last_leaf_found);
+				/* reuse the chain for cleanup mode */
+				chain->cur_depth = 1;
+				ipg = &chain->pages[0];
+				mdb_index_page_init(ipg);
+				ipg->pg = chain->last_leaf_found;
+				//printf("next on page %d\n",
+				if (!mdb_index_find_next_on_page(mdb, ipg))
+					return 0;
+			}
+		}
+		*row = mdb->pg_buf[ipg->offset + ipg->len - 1];
+		*pg = mdb_pg_get_int24_msb(mdb, ipg->offset + ipg->len - 4);
+		//printf("row = %d pg = %lu ipg->pg = %lu offset = %lu len = %d\n", *row, *pg, ipg->pg, ipg->offset, ipg->len);
+		col=g_ptr_array_index(idx->table->columns,idx->key_col_num[0]-1);
+		idx_sz = mdb_col_fixed_size(col);
+		/* handle compressed indexes, single key indexes only? */
+		if (idx->num_keys==1 && idx_sz>0 && ipg->len - 4 < idx_sz) {
+			//printf("short index found\n");
+			//buffer_dump(ipg->cache_value, 0, idx_sz);
+			memcpy(&ipg->cache_value[idx_sz - (ipg->len - 4)], &mdb->pg_buf[ipg->offset], ipg->len);
+			//buffer_dump(ipg->cache_value, 0, idx_sz);
+		} else {
+			idx_start = ipg->offset + (ipg->len - 4 - idx_sz);
+			memcpy(ipg->cache_value, &mdb->pg_buf[idx_start], idx_sz);
+		}
+
+		//idx_start = ipg->offset + (ipg->len - 4 - idx_sz);
+		passed = mdb_index_test_sargs(mdb, idx, ipg->cache_value, idx_sz);
+
+		ipg->offset += ipg->len;
+	} while (!passed);
+
+	//fprintf(stdout,"len = %d pos %d\n", ipg->len, ipg->mask_pos);
+	//buffer_dump(mdb->pg_buf, ipg->offset, ipg->offset+ipg->len-1);
+
+	return ipg->len;
+}
+/*
+ * XXX - FIX ME
+ * This function is grossly inefficient.  It scans the entire index building 
+ * an IndexChain to a specific row.  We should be checking the index pages 
+ * for matches against the indexed fields to find the proper leaf page, but
+ * getting it working first and then make it fast!
+ */
+int 
+mdb_index_find_row(MdbHandle *mdb, MdbIndex *idx, MdbIndexChain *chain, guint32 pg, guint16 row)
+{
+	MdbIndexPage *ipg;
+	int passed = 0;
+	guint32 datapg;
+	guint16 datarow;
+
+	ipg = mdb_index_read_bottom_pg(mdb, idx, chain);
+
+	do {
+		ipg->len = 0;
+		/*
+		 * if no more rows on this leaf, try to find a new leaf
+		 */
+		if (!mdb_index_find_next_on_page(mdb, ipg)) {
+			/* back to top? We're done */
+			if (chain->cur_depth==1)
+				return 0;
+
+			/* 
+			 * unwind the stack until we find something or reach 
+			 * the top.
+			 */
+			while (chain->cur_depth>1) {
+				chain->cur_depth--;
+				if (!(ipg = mdb_find_next_leaf(mdb, idx, chain)))
+					return 0;
+				mdb_index_find_next_on_page(mdb, ipg);
+			}
+			if (chain->cur_depth==1)
+				return 0;
+		}
+		/* test row and pg */
+		datarow = mdb->pg_buf[ipg->offset + ipg->len - 1];
+		datapg = mdb_pg_get_int24_msb(mdb, ipg->offset + ipg->len - 4);
+
+		if (datapg == pg && datarow == row) {
+			passed = 1;
+		}
+		ipg->offset += ipg->len;
+	} while (!passed);
+
+	/* index chain from root to leaf should now be in "chain" */
+	return 1;
+}
+
+void mdb_index_walk(MdbTableDef *table, MdbIndex *idx)
+{
+MdbHandle *mdb = table->entry->mdb;
+int cur_pos = 0;
+unsigned char marker;
+MdbColumn *col;
+unsigned int i;
+
+	if (idx->num_keys!=1) return;
+
+	mdb_read_pg(mdb, idx->first_pg);
+	cur_pos = 0xf8;
+	
+	for (i=0;i<idx->num_keys;i++) {
+		marker = mdb->pg_buf[cur_pos++];
+		col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1);
+		//printf("column %d coltype %d col_size %d (%d)\n",i,col->col_type, mdb_col_fixed_size(col), col->col_size);
+	}
+}
+void 
+mdb_index_dump(MdbTableDef *table, MdbIndex *idx)
+{
+	unsigned int i;
+	MdbColumn *col;
+
+	fprintf(stdout,"index number     %d\n", idx->index_num);
+	fprintf(stdout,"index name       %s\n", idx->name);
+	fprintf(stdout,"index first page %d\n", idx->first_pg);
+	fprintf(stdout,"index rows       %d\n", idx->num_rows);
+	if (idx->index_type==1) fprintf(stdout,"index is a primary key\n");
+	for (i=0;i<idx->num_keys;i++) {
+		col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1);
+		fprintf(stdout,"Column %s(%d) Sorted %s Unique: %s\n", 
+			col->name,
+			idx->key_col_num[i], 
+			idx->key_col_order[i]==MDB_ASC ? "ascending" : "descending",
+			idx->flags & MDB_IDX_UNIQUE ? "Yes" : "No"
+			);
+	}
+	mdb_index_walk(table, idx);
+}
+/*
+ * compute_cost tries to assign a cost to a given index using the sargs 
+ * available in this query.
+ *
+ * Indexes with no matching sargs are assigned 0
+ * Unique indexes are preferred over non-uniques
+ * Operator preference is equal, like, isnull, others 
+ */
+int mdb_index_compute_cost(MdbTableDef *table, MdbIndex *idx)
+{
+	unsigned int i;
+	MdbColumn *col;
+	MdbSarg *sarg = NULL;
+	int not_all_equal = 0;
+
+	if (!idx->num_keys) return 0;
+	if (idx->num_keys > 1) {
+		for (i=0;i<idx->num_keys;i++) {
+			col=g_ptr_array_index(table->columns,idx->key_col_num[i]-1);
+			if (col->sargs) sarg = g_ptr_array_index (col->sargs, 0);
+			if (!sarg || sarg->op != MDB_EQUAL) not_all_equal++;
+		}
+	}
+
+	col=g_ptr_array_index(table->columns,idx->key_col_num[0]-1);
+	/* 
+	 * if this is the first key column and there are no sargs,
+	 * then this index is useless.
+	 */
+	if (!col->num_sargs) return 0;
+
+	sarg = g_ptr_array_index (col->sargs, 0);
+
+	/*
+	 * a like with a wild card first is useless as a sarg */
+	if (sarg->op == MDB_LIKE && sarg->value.s[0]=='%')
+		return 0;
+
+	/*
+	 * this needs a lot of tweaking.
+	 */
+	if (idx->flags & MDB_IDX_UNIQUE) {
+		if (idx->num_keys == 1) {
+			//printf("op is %d\n", sarg->op);
+			switch (sarg->op) {
+				case MDB_EQUAL:
+					return 1; break;
+				case MDB_LIKE:
+					return 4; break;
+				case MDB_ISNULL:
+					return 12; break;
+				default:
+					return 8; break;
+			}
+		} else {
+			switch (sarg->op) {
+				case MDB_EQUAL:
+					if (not_all_equal) return 2; 
+					else return 1;
+					break;
+				case MDB_LIKE:
+					return 6; break;
+				case MDB_ISNULL:
+					return 12; break;
+				default:
+					return 9; break;
+			}
+		}
+	} else {
+		if (idx->num_keys == 1) {
+			switch (sarg->op) {
+				case MDB_EQUAL:
+					return 2; break;
+				case MDB_LIKE:
+					return 5; break;
+				case MDB_ISNULL:
+					return 12; break;
+				default:
+					return 10; break;
+			}
+		} else {
+			switch (sarg->op) {
+				case MDB_EQUAL:
+					if (not_all_equal) return 3; 
+					else return 2;
+					break;
+				case MDB_LIKE:
+					return 7; break;
+				case MDB_ISNULL:
+					return 12; break;
+				default:
+					return 11; break;
+			}
+		}
+	}
+	return 0;
+}
+/*
+ * choose_index runs mdb_index_compute_cost for each available index and picks
+ * the best.
+ *
+ * Returns strategy to use (table scan, or index scan)
+ */
+MdbStrategy 
+mdb_choose_index(MdbTableDef *table, int *choice)
+{
+	unsigned int i;
+	MdbIndex *idx;
+	int cost = 0;
+	int least = 99;
+
+	*choice = -1;
+	for (i=0;i<table->num_idxs;i++) {
+		idx = g_ptr_array_index (table->indices, i);
+		cost = mdb_index_compute_cost(table, idx);
+		//printf("cost for %s is %d\n", idx->name, cost);
+		if (cost && cost < least) {
+			least = cost;
+			*choice = i;
+		}
+	}
+	/* and the winner is: *choice */
+	if (least==99) return MDB_TABLE_SCAN;
+	return MDB_INDEX_SCAN;
+}
+void
+mdb_index_scan_init(MdbHandle *mdb, MdbTableDef *table)
+{
+	int i;
+
+	if (mdb_get_option(MDB_USE_INDEX) && mdb_choose_index(table, &i) == MDB_INDEX_SCAN) {
+		table->strategy = MDB_INDEX_SCAN;
+		table->scan_idx = g_ptr_array_index (table->indices, i);
+		table->chain = g_malloc0(sizeof(MdbIndexChain));
+		table->mdbidx = mdb_clone_handle(mdb);
+		mdb_read_pg(table->mdbidx, table->scan_idx->first_pg);
+		//printf("best index is %s\n",table->scan_idx->name);
+	}
+	//printf("TABLE SCAN? %d\n", table->strategy);
+}
+void 
+mdb_index_scan_free(MdbTableDef *table)
+{
+	if (table->chain) {
+		g_free(table->chain);
+		table->chain = NULL;
+	}
+	if (table->mdbidx) {
+		mdb_close(table->mdbidx);
+		table->mdbidx = NULL;
+	}
+}
+
+void mdb_free_indices(GPtrArray *indices)
+{
+	unsigned int i;
+
+	if (!indices) return;
+	for (i=0; i<indices->len; i++)
+		g_free (g_ptr_array_index(indices, i));
+	g_ptr_array_free(indices, TRUE);
+}
diff --git a/providers/mdb/libmdb-src/kkd.c b/providers/mdb/libmdb-src/kkd.c
new file mode 100644
index 0000000..b1b7f12
--- /dev/null
+++ b/providers/mdb/libmdb-src/kkd.c
@@ -0,0 +1,149 @@
+/* MDB Tools - A library for reading MS Access database file
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "mdbtools.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+
+/*
+** Note: This code is mostly garbage right now...just a test to parse out the
+**       KKD structures.
+*/
+
+static GArray *mdb_get_column_props(MdbCatalogEntry *entry, int start)
+{
+int pos, cnt=0;
+int len, tmp, cplen;
+MdbColumnProp prop;
+MdbHandle *mdb = entry->mdb;
+
+	entry->props = g_array_new(FALSE,FALSE,sizeof(MdbColumnProp));
+	len = mdb_pg_get_int16(mdb,start);
+	pos = start + 6;
+	while (pos < start+len) {
+		tmp = mdb_pg_get_int16(mdb,pos); /* length of string */
+		pos += 2;
+		cplen = tmp > MDB_MAX_OBJ_NAME ? MDB_MAX_OBJ_NAME : tmp;
+		g_memmove(prop.name,&mdb->pg_buf[pos],cplen);
+		prop.name[cplen]='\0';
+		pos += tmp; 
+		g_array_append_val(entry->props, prop.name);
+		cnt++;
+	}
+	entry->num_props = cnt;
+	return entry->props;
+}
+
+static GHashTable *mdb_get_column_def(MdbCatalogEntry *entry, int start)
+{
+GHashTable *hash = NULL;
+MdbHandle *mdb = entry->mdb;
+MdbColumnProp prop;
+int tmp, pos, col_num, val_len, i;
+int len, col_type;
+unsigned char c;
+int end;
+
+	fprintf(stdout,"\n data\n");
+	fprintf(stdout,"-------\n");
+	len = mdb_pg_get_int16(mdb,start);
+	fprintf(stdout,"length = %3d\n",len);
+	pos = start + 6;
+	end = start + len;
+	while (pos < end) {
+		fprintf(stdout,"pos = %3d\n",pos);
+		start = pos;
+		tmp = mdb_pg_get_int16(mdb,pos); /* length of field */
+		pos += 2;
+		col_type = mdb_pg_get_int16(mdb,pos); /* ??? */
+		pos += 2;
+		col_num = 0;
+		if (col_type) {
+			col_num = mdb_pg_get_int16(mdb,pos); 
+			pos += 2;
+		}
+		val_len = mdb_pg_get_int16(mdb,pos);
+		pos += 2;
+		fprintf(stdout,"length = %3d %04x %2d %2d ",tmp, col_type, col_num, val_len);
+		for (i=0;i<val_len;i++) {
+			c = mdb->pg_buf[pos+i];
+			if (isprint(c))
+				fprintf(stdout,"  %c",c);
+			else 
+				fprintf(stdout," %02x",c);
+
+		}
+		pos = start + tmp; 
+		prop = g_array_index(entry->props,MdbColumnProp,col_num);
+		fprintf(stdout," Property %s",prop.name); 
+		fprintf(stdout,"\n");
+	}
+	return hash;
+}
+void mdb_kkd_dump(MdbCatalogEntry *entry)
+{
+int rows;
+int kkd_start, kkd_end;
+int i, tmp, pos, row_type, datapos=0;
+MdbColumnProp prop;
+MdbHandle *mdb = entry->mdb;
+int rowid = entry->kkd_rowid;
+
+
+	mdb_read_pg(mdb, entry->kkd_pg);
+	rows = mdb_get_int16(mdb->pg_buf, 8);
+	fprintf(stdout,"number of rows = %d\n",rows);
+	kkd_start = mdb_get_int16(mdb->pg_buf, 10+rowid*2);
+	fprintf(stdout,"kkd start = %d %04x\n",kkd_start,kkd_start);
+	kkd_end = mdb->fmt->pg_size;
+	for (i=0;i<rows;i++) {
+		tmp = mdb_get_int16(mdb->pg_buf, 10+i*2);
+		if (tmp < mdb->fmt->pg_size &&
+		    tmp > kkd_start &&
+		    tmp < kkd_end) {
+			kkd_end = tmp;
+		}
+	}
+	fprintf(stdout,"kkd end = %d %04x\n",kkd_end,kkd_end);
+	pos = kkd_start + 4; /* 4 = K K D \0 */
+	while (pos < kkd_end) {
+		tmp = mdb_pg_get_int16(mdb,pos);
+		row_type = mdb_pg_get_int16(mdb,pos+4);
+		fprintf(stdout,"row size = %3d type = 0x%02x\n",tmp,row_type);
+		if (row_type==0x80)  {
+			fprintf(stdout,"\nColumn Properties\n");
+			fprintf(stdout,"-----------------\n");
+			mdb_get_column_props(entry,pos);
+			for (i=0;i<entry->num_props;i++) {
+				prop = g_array_index(entry->props,MdbColumnProp,i);
+				fprintf(stdout,"%3d %s\n",i,prop.name); 
+			}
+		}
+		if (row_type==0x01) datapos = pos;
+		pos += tmp;
+	}
+	
+	if (datapos) {
+		mdb_get_column_def(entry, datapos);
+	}
+}
+
diff --git a/providers/mdb/libmdb-src/like.c b/providers/mdb/libmdb-src/like.c
new file mode 100644
index 0000000..0a23d45
--- /dev/null
+++ b/providers/mdb/libmdb-src/like.c
@@ -0,0 +1,78 @@
+/* MDB Tools - A library for reading MS Access database file
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <mdbtools.h>
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+/**
+ * mdb_like_cmp
+ * @s: String to search within.
+ * @r: Search pattern.
+ *
+ * Tests the string @s to see if it matches the search pattern @r.  In the
+ * search pattern, a percent sign indicates matching on any number of
+ * characters, and an underscore indicates matching any single character.
+ *
+ * Returns: 1 if the string matches, 0 if the string does not match.
+ */
+int mdb_like_cmp(char *s, char *r)
+{
+	unsigned int i;
+	int ret;
+
+	mdb_debug(MDB_DEBUG_LIKE, "comparing %s and %s", s, r);
+	switch (r[0]) {
+		case '\0':
+			if (s[0]=='\0') {
+				return 1;
+			} else {
+				return 0;
+			}
+		case '_':
+			/* skip one character */
+			return mdb_like_cmp(&s[1],&r[1]);
+		case '%':
+			/* skip any number of characters */
+			/* the strlen(s)+1 is important so the next call can */
+			/* if there are trailing characters */
+			for(i=0;i<strlen(s)+1;i++) {
+				if (mdb_like_cmp(&s[i],&r[1])) {
+					return 1;
+				}
+			}
+			return 0;
+		default:
+			for(i=0;i<strlen(r);i++) {
+				if (r[i]=='_' || r[i]=='%') break;
+			}
+			if (strncmp(s,r,i)) {
+				return 0;
+			} else {
+				mdb_debug(MDB_DEBUG_LIKE, "at pos %d comparing %s and %s", i, &s[i], &r[i]);
+				ret = mdb_like_cmp(&s[i],&r[i]);
+				mdb_debug(MDB_DEBUG_LIKE, "returning %d (%s and %s)", ret, &s[i], &r[i]);
+				return ret;
+			}
+	}
+}
diff --git a/providers/mdb/libmdb-src/map.c b/providers/mdb/libmdb-src/map.c
new file mode 100644
index 0000000..6c3abe4
--- /dev/null
+++ b/providers/mdb/libmdb-src/map.c
@@ -0,0 +1,133 @@
+/* MDB Tools - A library for reading MS Access database file
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "mdbtools.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+static guint32 
+mdb_map_find_next0(MdbHandle *mdb, unsigned char *map, unsigned int map_sz, guint32 start_pg)
+{
+	guint32 pgnum, i, usage_bitlen;
+	unsigned char *usage_bitmap;
+
+	pgnum = mdb_get_int32(map, 1);
+	usage_bitmap = map + 5;
+	usage_bitlen = (map_sz - 5) * 8;
+
+	i = (start_pg >= pgnum) ? start_pg-pgnum+1 : 0;
+	for (; i<usage_bitlen; i++) {
+		if (usage_bitmap[i/8] & (1 << (i%8))) {
+			return pgnum + i;
+		}
+	}
+	/* didn't find anything */
+	return 0;
+}
+static int 
+mdb_map_find_next1(MdbHandle *mdb, unsigned char *map, unsigned int map_sz, guint32 start_pg)
+{
+	guint32 map_ind, max_map_pgs, offset, usage_bitlen;
+
+	/*
+	* start_pg will tell us where to (re)start the scan
+	* for the next data page.  each usage_map entry points to a
+	* 0x05 page which bitmaps (mdb->fmt->pg_size - 4) * 8 pages.
+	*
+	* map_ind gives us the starting usage_map entry
+	* offset gives us a page offset into the bitmap
+	*/
+	usage_bitlen = (mdb->fmt->pg_size - 4) * 8;
+	max_map_pgs = (map_sz - 1) / 4;
+	map_ind = (start_pg + 1) / usage_bitlen;
+	offset = (start_pg + 1) % usage_bitlen;
+
+	for (; map_ind<max_map_pgs; map_ind++) {
+		unsigned char *usage_bitmap;
+		guint32 i, map_pg;
+
+		if (!(map_pg = mdb_get_int32(map, (map_ind*4)+1))) {
+			continue;
+		}
+		if(mdb_read_alt_pg(mdb, map_pg) != mdb->fmt->pg_size) {
+			fprintf(stderr, "Oops! didn't get a full page at %d\n", map_pg);
+			exit(1);
+		} 
+
+		usage_bitmap = mdb->alt_pg_buf + 4;
+		for (i=offset; i<usage_bitlen; i++) {
+			if (usage_bitmap[i/8] & (1 << (i%8))) {
+				return map_ind*usage_bitlen + i;
+			}
+		}
+		offset = 0;
+	}
+	/* didn't find anything */
+	return 0;
+}
+guint32 
+mdb_map_find_next(MdbHandle *mdb, unsigned char *map, unsigned int map_sz, guint32 start_pg)
+{
+	if (map[0] == 0) {
+		return mdb_map_find_next0(mdb, map, map_sz, start_pg);
+	} else if (map[0] == 1) {
+		return mdb_map_find_next1(mdb, map, map_sz, start_pg);
+	}
+
+	fprintf(stderr, "Warning: unrecognized usage map type: %d\n", map[0]);
+	return -1;
+}
+guint32
+mdb_alloc_page(MdbTableDef *table)
+{
+	printf("Allocating new page\n");
+	return 0;
+}
+guint32 
+mdb_map_find_next_freepage(MdbTableDef *table, int row_size)
+{
+	MdbCatalogEntry *entry = table->entry;
+	MdbHandle *mdb = entry->mdb;
+	guint32 pgnum;
+	guint32 cur_pg = 0;
+	int free_space;
+
+	do {
+		pgnum = mdb_map_find_next(mdb, 
+				table->free_usage_map, 
+				table->freemap_sz, cur_pg);
+		//printf("looking at page %d\n", pgnum);
+		if (!pgnum) {
+			/* allocate new page */
+			pgnum = mdb_alloc_page(table);
+			return pgnum;
+		}
+		cur_pg = pgnum;
+
+		mdb_read_pg(mdb, pgnum);
+		free_space = mdb_pg_get_freespace(mdb);
+		
+	} while (free_space < row_size);
+
+	//printf("page %d has %d bytes left\n", pgnum, free_space);
+
+	return pgnum;
+}
diff --git a/providers/mdb/libmdb-src/mem.c b/providers/mdb/libmdb-src/mem.c
new file mode 100644
index 0000000..35b3c51
--- /dev/null
+++ b/providers/mdb/libmdb-src/mem.c
@@ -0,0 +1,52 @@
+/* MDB Tools - A library for reading MS Access database files
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+#ifdef JAVA
+#include "javadefines.h"
+#else
+#include "mdbtools.h"
+#include <locale.h>
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+#endif  /* JAVA */
+/**
+ * mdb_init:
+ *
+ * Initializes the LibMDB library.  This function should be called exactly once
+ * by calling program and prior to any other function.
+ *
+ **/
+/* METHOD */ void mdb_init()
+{
+	mdb_init_backends();
+}
+
+/**
+ * mdb_exit:
+ *
+ * Cleans up the LibMDB library.  This function should be called exactly once
+ * by the calling program prior to exiting (or prior to final use of LibMDB 
+ * functions).
+ *
+ **/
+/* METHOD */ void mdb_exit()
+{
+	mdb_remove_backends();
+}
diff --git a/providers/mdb/libmdb-src/money.c b/providers/mdb/libmdb-src/money.c
new file mode 100644
index 0000000..2b0300b
--- /dev/null
+++ b/providers/mdb/libmdb-src/money.c
@@ -0,0 +1,138 @@
+/* MDB Tools - A library for reading MS Access database file
+ * Copyright (C) 1998-1999  Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include "mdbtools.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+#define MAXPRECISION 19
+/* 
+** these routines are copied from the freetds project which does something
+** very similiar
+*/
+
+static int multiply_byte(unsigned char *product, int num, unsigned char *multiplier);
+static int do_carry(unsigned char *product);
+static char *array_to_string(unsigned char *array, int unsigned scale, int neg);
+
+/**
+ * mdb_money_to_string
+ * @mdb: Handle to open MDB database file
+ * @start: Offset of the field within the current page
+ *
+ * Returns: the allocated string that has received the value.
+ */
+char *mdb_money_to_string(MdbHandle *mdb, int start)
+{
+	const int num_bytes = 8;
+	int i;
+	int neg=0;
+	unsigned char multiplier[MAXPRECISION], temp[MAXPRECISION];
+	unsigned char product[MAXPRECISION];
+	unsigned char money[num_bytes];
+
+	memset(multiplier,0,MAXPRECISION);
+	memset(product,0,MAXPRECISION);
+	multiplier[0]=1;
+	memcpy(money, mdb->pg_buf + start, num_bytes);
+
+	/* Perform two's complement for negative numbers */
+	if (money[7] & 0x80) {
+		neg = 1;
+		for (i=0;i<num_bytes;i++) {
+			money[i] = ~money[i];
+		}
+		for (i=0; i<num_bytes; i++) {
+			money[i] ++;
+			if (money[i]!=0) break;
+		}
+	}
+
+	for (i=0;i<num_bytes;i++) {
+		/* product += multiplier * current byte */
+		multiply_byte(product, money[i], multiplier);
+
+		/* multiplier = multiplier * 256 */
+		memcpy(temp, multiplier, MAXPRECISION);
+		memset(multiplier,0,MAXPRECISION);
+		multiply_byte(multiplier, 256, temp);
+	}
+	return array_to_string(product, 4, neg);
+}
+static int multiply_byte(unsigned char *product, int num, unsigned char *multiplier)
+{
+	unsigned char number[3];
+	unsigned int i, j;
+
+	number[0]=num%10;
+	number[1]=(num/10)%10;
+	number[2]=(num/100)%10;
+
+	for (i=0;i<MAXPRECISION;i++) {
+		if (multiplier[i] == 0) continue;
+		for (j=0;j<3;j++) {
+			if (number[j] == 0) continue;
+			product[i+j] += multiplier[i]*number[j];
+		}
+		do_carry(product);
+	}
+	return 0;
+}
+static int do_carry(unsigned char *product)
+{
+	unsigned int j;
+
+	for (j=0;j<MAXPRECISION-1;j++) {
+		if (product[j]>9) {
+			product[j+1]+=product[j]/10;
+			product[j]=product[j]%10;
+		}
+	}
+	if (product[j]>9) {
+		product[j]=product[j]%10;
+	}
+	return 0;
+}
+static char *array_to_string(unsigned char *array, unsigned int scale, int neg)
+{
+	char *s;
+	unsigned int top, i, j=0;
+	
+	for (top=MAXPRECISION;(top>0) && (top-1>scale) && !array[top-1];top--);
+
+	s = (char *) g_malloc(22);
+
+	if (neg)
+		s[j++] = '-';
+
+	if (top == 0) {
+		s[j++] = '0';
+	} else {
+		for (i=top; i>0; i--) {
+			if (i == scale) s[j++]='.';
+			s[j++]=array[i-1]+'0';
+		}
+	}
+	s[j]='\0';
+
+	return s;
+}
diff --git a/providers/mdb/libmdb-src/options.c b/providers/mdb/libmdb-src/options.c
new file mode 100644
index 0000000..2c16526
--- /dev/null
+++ b/providers/mdb/libmdb-src/options.c
@@ -0,0 +1,87 @@
+/* MDB Tools - A library for reading MS Access database file
+ * Copyright (C) 2004 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include <mdbtools.h>
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+#define DEBUG 1
+
+static unsigned long opts;
+static int optset;
+
+static void load_options();
+
+void
+mdb_debug(int klass, char *fmt, ...)
+{
+#ifdef DEBUG
+	va_list ap;
+
+	if (!optset) load_options();
+	if (klass & opts) {	
+    	va_start(ap, fmt);
+    	vfprintf (stdout,fmt, ap);
+    	va_end(ap);
+    	fprintf(stdout,"\n");
+	}
+#endif
+}
+
+static void
+load_options()
+{
+	char *opt;
+	char *s;
+
+    if (!optset && (s=getenv("MDBOPTS"))) {
+		opt = strtok(s, ":");
+		do {
+        	if (!strcmp(opt, "use_index")) opts |= MDB_USE_INDEX;
+        	if (!strcmp(opt, "no_memo")) opts |= MDB_NO_MEMO;
+        	if (!strcmp(opt, "debug_like")) opts |= MDB_DEBUG_LIKE;
+        	if (!strcmp(opt, "debug_write")) opts |= MDB_DEBUG_WRITE;
+        	if (!strcmp(opt, "debug_usage")) opts |= MDB_DEBUG_USAGE;
+        	if (!strcmp(opt, "debug_ole")) opts |= MDB_DEBUG_OLE;
+        	if (!strcmp(opt, "debug_row")) opts |= MDB_DEBUG_ROW;
+        	if (!strcmp(opt, "debug_all")) {
+				opts |= MDB_DEBUG_LIKE;
+				opts |= MDB_DEBUG_WRITE;
+				opts |= MDB_DEBUG_USAGE;
+				opts |= MDB_DEBUG_OLE;
+				opts |= MDB_DEBUG_ROW;
+			}
+			opt = strtok(NULL,":");
+		} while (opt);
+    }
+	optset = 1;
+}
+int
+mdb_get_option(unsigned long optnum)
+{
+	if (!optset) load_options();
+	return ((opts & optnum) > 0);
+}
diff --git a/providers/mdb/libmdb-src/props.c b/providers/mdb/libmdb-src/props.c
new file mode 100644
index 0000000..d676f9d
--- /dev/null
+++ b/providers/mdb/libmdb-src/props.c
@@ -0,0 +1,129 @@
+/* MDB Tools - A library for reading MS Access database file
+ * Copyright (C) 2000 Brian Bruns
+ *
+ *
+ * This library is free software; you can redistribute it and/or 
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "mdbtools.h"
+
+GPtrArray *
+mdb_read_props_list(gchar *kkd, int len)
+{
+	guint32 record_len;
+	int pos = 0;
+	gchar *name;
+	GPtrArray *names = NULL;
+	int i = 0;
+
+	names = g_ptr_array_new();
+#ifdef MDB_DEBUG
+	buffer_dump(kkd, 0, len - 1);
+#endif
+	pos = 0;
+	while (pos < len) {
+		record_len = mdb_get_int16(kkd, pos);
+		pos += 2;
+#ifdef MDB_DEBUG
+		printf("%02d ",i++);
+		buffer_dump(kkd, pos - 2, pos + record_len - 1);
+#endif
+		name = g_malloc(record_len + 1);
+		strncpy(name, &kkd[pos], record_len);
+		name[record_len] = '\0';
+		pos += record_len;
+		g_ptr_array_add(names, name);
+#ifdef MDB_DEBUG
+		printf("new len = %d\n", names->len);
+#endif
+	}
+	return names;
+}
+void
+mdb_free_props(MdbProperties *props)
+{
+	if (!props) return;
+
+	if (props->name) g_free(props->name);
+	g_free(props);
+}
+MdbProperties *
+mdb_alloc_props()
+{
+	MdbProperties *props;
+
+	props = g_malloc0(sizeof(MdbProperties));
+
+	return props;
+}
+MdbProperties * 
+mdb_read_props(MdbHandle *mdb, GPtrArray *names, gchar *kkd, int len)
+{
+	guint32 record_len, name_len;
+	int pos = 0;
+	int elem, dtype, dsize;
+	gchar *name, *value;
+	MdbProperties *props;
+	int i = 0;
+
+#ifdef MDB_DEBUG
+	buffer_dump(kkd, 0, len - 1);
+#endif
+	pos = 0;
+
+	/* skip the name record */
+	record_len = mdb_get_int16(kkd, pos);
+	pos += 4;
+	name_len = mdb_get_int16(kkd, pos);
+	pos += 2;
+	props = mdb_alloc_props();
+	if (name_len) {
+		props->name = g_malloc(name_len + 1);
+		strncpy(props->name, &kkd[pos], name_len);
+		props->name[name_len]='\0';
+	}
+	pos += name_len;
+
+	props->hash = g_hash_table_new(g_str_hash, g_str_equal);
+
+	while (pos < len) {
+		record_len = mdb_get_int16(kkd, pos);
+		elem = mdb_get_int16(kkd, pos + 4);
+		dtype = kkd[pos + 3];
+		dsize = mdb_get_int16(kkd, pos + 6);
+		value = g_malloc(dsize + 1);
+		strncpy(value, &kkd[pos + 8], dsize);
+		value[dsize] = '\0';
+		name = g_ptr_array_index(names,elem);
+#ifdef MDB_DEBUG
+		printf("%02d ",i++);
+		buffer_dump(kkd, pos, pos + record_len - 1);
+		printf("elem %d dsize %d dtype %d\n", elem, dsize, dtype);
+#endif
+		if (dtype == MDB_MEMO) dtype = MDB_TEXT;
+		if (dtype == MDB_BOOL) {
+			g_hash_table_insert(props->hash, g_strdup(name),
+				g_strdup(kkd[pos + 8] ? "yes" : "no"));
+		} else {
+			g_hash_table_insert(props->hash, g_strdup(name),
+			  mdb_col_to_string(mdb, kkd, pos + 8, dtype, dsize));
+		}
+		g_free(value);
+		pos += record_len;
+	}
+	return props;
+	
+}
diff --git a/providers/mdb/libmdb-src/sargs.c b/providers/mdb/libmdb-src/sargs.c
new file mode 100644
index 0000000..fc38369
--- /dev/null
+++ b/providers/mdb/libmdb-src/sargs.c
@@ -0,0 +1,266 @@
+/* MDB Tools - A library for reading MS Access database file
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+/*
+ * code for handling searchable arguments (sargs) used primary by the sql 
+ * engine to support where clause handling.  The sargs are configured in 
+ * a tree with AND/OR operators connecting the child nodes. NOT operations
+ * have only one child on the left side.  Logical operators (=,<,>,etc..)
+ * have no children.
+ *
+ * datatype support is a bit weak at this point.  To add more types create
+ * a mdb_test_[type]() function and invoke it from mdb_test_sarg()
+ */
+#include "mdbtools.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+void
+mdb_sql_walk_tree(MdbSargNode *node, MdbSargTreeFunc func, gpointer data)
+{
+	if (func(node, data))
+		return;
+	if (node->left) mdb_sql_walk_tree(node->left, func, data);
+	if (node->right) mdb_sql_walk_tree(node->right, func, data);
+}
+int 
+mdb_test_string(MdbSargNode *node, char *s)
+{
+int rc;
+
+	if (node->op == MDB_LIKE) {
+		return mdb_like_cmp(s,node->value.s);
+	}
+	rc = strncmp(node->value.s, s, 255);
+	switch (node->op) {
+		case MDB_EQUAL:
+			if (rc==0) return 1;
+			break;
+		case MDB_GT:
+			if (rc<0) return 1;
+			break;
+		case MDB_LT:
+			if (rc>0) return 1;
+			break;
+		case MDB_GTEQ:
+			if (rc<=0) return 1;
+			break;
+		case MDB_LTEQ:
+			if (rc>=0) return 1;
+			break;
+		default:
+			fprintf(stderr, "Calling mdb_test_sarg on unknown operator.  Add code to mdb_test_string() for operator %d\n",node->op);
+			break;
+	}
+	return 0;
+}
+int mdb_test_int(MdbSargNode *node, gint32 i)
+{
+	switch (node->op) {
+		case MDB_EQUAL:
+			//fprintf(stderr, "comparing %ld and %ld\n", i, node->value.i);
+			if (node->value.i == i) return 1;
+			break;
+		case MDB_GT:
+			if (node->value.i < i) return 1;
+			break;
+		case MDB_LT:
+			if (node->value.i > i) return 1;
+			break;
+		case MDB_GTEQ:
+			if (node->value.i <= i) return 1;
+			break;
+		case MDB_LTEQ:
+			if (node->value.i >= i) return 1;
+			break;
+		default:
+			fprintf(stderr, "Calling mdb_test_sarg on unknown operator.  Add code to mdb_test_int() for operator %d\n",node->op);
+			break;
+	}
+	return 0;
+}
+#if 0
+#endif
+int
+mdb_find_indexable_sargs(MdbSargNode *node, gpointer data)
+{
+	MdbSarg sarg;
+
+	if (node->op == MDB_OR || node->op == MDB_NOT) return 1;
+
+	/* 
+	 * right now all we do is look for sargs that are anded together from
+	 * the root.  Later we may put together OR ops into a range, and then 
+	 * range scan the leaf pages. That is col1 = 2 or col1 = 4 becomes
+	 * col1 >= 2 and col1 <= 4 for the purpose of index scans, and then
+	 * extra rows are thrown out when the row is tested against the main
+	 * sarg tree.  range scans are generally only a bit better than table
+	 * scanning anyway.
+	 *
+	 * also, later we should support the NOT operator, but it's generally
+	 * a pretty worthless test for indexes, ie NOT col1 = 3, we are 
+	 * probably better off table scanning.
+	 */
+	if (mdb_is_relational_op(node->op) && node->col) {
+		//printf("op = %d value = %s\n", node->op, node->value.s);
+		sarg.op = node->op;
+		sarg.value = node->value;
+		mdb_add_sarg(node->col, &sarg);
+	}
+	return 0;
+}
+int 
+mdb_test_sarg(MdbHandle *mdb, MdbColumn *col, MdbSargNode *node, MdbField *field)
+{
+	char tmpbuf[256];
+
+	if (node->op == MDB_ISNULL) {
+		if (field->is_null) return 0;
+		else return 1;
+	} else if (node->op == MDB_NOTNULL) {
+		if (field->is_null) return 1;
+		else return 0;
+	}
+	switch (col->col_type) {
+		case MDB_BOOL:
+			return mdb_test_int(node, !field->is_null);
+			break;
+		case MDB_BYTE:
+			return mdb_test_int(node, (gint32)((char *)field->value)[0]);
+			break;
+		case MDB_INT:
+			return mdb_test_int(node, (gint32)mdb_get_int16(field->value, 0));
+			break;
+		case MDB_LONGINT:
+			return mdb_test_int(node, (gint32)mdb_get_int32(field->value, 0));
+			break;
+		case MDB_TEXT:
+			mdb_unicode2ascii(mdb, field->value, field->siz, tmpbuf, 256);
+			return mdb_test_string(node, tmpbuf);
+		default:
+			fprintf(stderr, "Calling mdb_test_sarg on unknown type.  Add code to mdb_test_sarg() for type %d\n",col->col_type);
+			break;
+	}
+	return 1;
+}
+int
+mdb_find_field(int col_num, MdbField *fields, int num_fields)
+{
+	int i;
+
+	for (i=0;i<num_fields;i++) {
+		if (fields[i].colnum == col_num) return i;
+	}
+	return -1;
+}
+int
+mdb_test_sarg_node(MdbHandle *mdb, MdbSargNode *node, MdbField *fields, int num_fields)
+{
+	int elem;
+	MdbColumn *col;
+	int rc;
+
+	if (mdb_is_relational_op(node->op)) {
+		col = node->col;
+		/* for const = const expressions */
+		if (!col) {
+			return (node->value.i);
+		}
+		elem = mdb_find_field(col->col_num, fields, num_fields);
+		if (!mdb_test_sarg(mdb, col, node, &fields[elem])) 
+			return 0;
+	} else { /* logical op */
+		switch (node->op) {
+		case MDB_NOT:
+			rc = mdb_test_sarg_node(mdb, node->left, fields, num_fields);
+			return !rc;
+			break;
+		case MDB_AND:
+			if (!mdb_test_sarg_node(mdb, node->left, fields, num_fields))
+				return 0;
+			return mdb_test_sarg_node(mdb, node->right, fields, num_fields);
+			break;
+		case MDB_OR:
+			if (mdb_test_sarg_node(mdb, node->left, fields, num_fields))
+				return 1;
+			return mdb_test_sarg_node(mdb, node->right, fields, num_fields);
+			break;
+		}
+	}
+	return 1;
+}
+int 
+mdb_test_sargs(MdbTableDef *table, MdbField *fields, int num_fields)
+{
+	MdbSargNode *node;
+	MdbCatalogEntry *entry = table->entry;
+	MdbHandle *mdb = entry->mdb;
+
+	node = table->sarg_tree;
+
+	/* there may not be a sarg tree */
+	if (!node) return 1;
+
+	return mdb_test_sarg_node(mdb, node, fields, num_fields);
+}
+#if 0
+int mdb_test_sargs(MdbHandle *mdb, MdbColumn *col, int offset, int len)
+{
+MdbSarg *sarg;
+int i;
+
+	for (i=0;i<col->num_sargs;i++) {
+		sarg = g_ptr_array_index (col->sargs, i);
+		if (!mdb_test_sarg(mdb, col, sarg, offset, len)) {
+			/* sarg didn't match, no sense going on */
+			return 0;	
+		}
+	}
+
+	return 1;
+}
+#endif
+int mdb_add_sarg(MdbColumn *col, MdbSarg *in_sarg)
+{
+MdbSarg *sarg;
+        if (!col->sargs) {
+		col->sargs = g_ptr_array_new();
+	}
+	sarg = g_memdup(in_sarg,sizeof(MdbSarg));
+        g_ptr_array_add(col->sargs, sarg);
+	col->num_sargs++;
+
+	return 1;
+}
+int mdb_add_sarg_by_name(MdbTableDef *table, char *colname, MdbSarg *in_sarg)
+{
+	MdbColumn *col;
+	unsigned int i;
+
+	for (i=0;i<table->num_cols;i++) {
+		col = g_ptr_array_index (table->columns, i);
+		if (!strcasecmp(col->name,colname)) {
+			return mdb_add_sarg(col, in_sarg);
+		}
+	}
+	/* else didn't find the column return 0! */
+	return 0;
+}
diff --git a/providers/mdb/libmdb-src/stats.c b/providers/mdb/libmdb-src/stats.c
new file mode 100644
index 0000000..1abf285
--- /dev/null
+++ b/providers/mdb/libmdb-src/stats.c
@@ -0,0 +1,74 @@
+/* MDB Tools - A library for reading MS Access database files
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "mdbtools.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+/**
+ * mdb_stats_on:
+ * @mdb: Handle to the (open) MDB file to collect stats on.
+ *
+ * Begins collection of statistics on an MDBHandle.
+ *
+ * Statistics in LibMDB will track the number of reads from the MDB file.  The
+ * collection of statistics is started and stopped with the mdb_stats_on and
+ * mdb_stats_off functions.  Collected statistics are accessed by reading the
+ * MdbStatistics structure or calling mdb_dump_stats.
+ * 
+ */
+void
+mdb_stats_on(MdbHandle *mdb)
+{
+	if (!mdb->stats) 
+		mdb->stats = g_malloc0(sizeof(MdbStatistics));
+
+	mdb->stats->collect = TRUE;
+}
+/**
+ * mdb_stats_off:
+ * @mdb: pointer to handle of MDB file with active stats collection.
+ *
+ * Turns off statistics collection.
+ *
+ * If mdb_stats_off is not called, statistics will be turned off when handle
+ * is freed using mdb_close.
+ **/
+void
+mdb_stats_off(MdbHandle *mdb)
+{
+	if (!mdb->stats) return;
+
+	mdb->stats->collect = FALSE;
+}
+/**
+ * mdb_dump_stats:
+ * @mdb: pointer to handle of MDB file with active stats collection.
+ *
+ * Dumps current statistics to stdout.
+ **/
+void
+mdb_dump_stats(MdbHandle *mdb)
+{
+	if (!mdb->stats) return;
+
+	fprintf(stdout, "Physical Page Reads: %lu\n", mdb->stats->pg_reads);
+}
diff --git a/providers/mdb/libmdb-src/table.c b/providers/mdb/libmdb-src/table.c
new file mode 100644
index 0000000..3119432
--- /dev/null
+++ b/providers/mdb/libmdb-src/table.c
@@ -0,0 +1,371 @@
+/* MDB Tools - A library for reading MS Access database file
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "mdbtools.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+
+static gint mdb_col_comparer(MdbColumn **a, MdbColumn **b)
+{
+	if ((*a)->col_num > (*b)->col_num)
+		return 1;
+	else if ((*a)->col_num < (*b)->col_num)
+		return -1;
+	else
+		return 0;
+}
+
+unsigned char mdb_col_needs_size(int col_type)
+{
+	if (col_type == MDB_TEXT) {
+		return TRUE;
+	} else {
+		return FALSE;
+	}
+}
+
+MdbTableDef *mdb_alloc_tabledef(MdbCatalogEntry *entry)
+{
+	MdbTableDef *table;
+
+	table = (MdbTableDef *) g_malloc0(sizeof(MdbTableDef));
+	table->entry=entry;
+	strcpy(table->name, entry->object_name);
+
+	return table;	
+}
+void mdb_free_tabledef(MdbTableDef *table)
+{
+	if (!table) return;
+	if (table->is_temp_table) {
+		unsigned int i;
+		/* Temp table pages are being stored in memory */
+		for (i=0; i<table->temp_table_pages->len; i++)
+			g_free(g_ptr_array_index(table->temp_table_pages,i));
+		g_ptr_array_free(table->temp_table_pages, TRUE);
+		/* Temp tables use dummy entries */
+		g_free(table->entry);
+	}
+	mdb_free_columns(table->columns);
+	mdb_free_indices(table->indices);
+	g_free(table->usage_map);
+	g_free(table->free_usage_map);
+	g_free(table);
+}
+MdbTableDef *mdb_read_table(MdbCatalogEntry *entry)
+{
+	MdbTableDef *table;
+	MdbHandle *mdb = entry->mdb;
+	MdbFormatConstants *fmt = mdb->fmt;
+	int len, row_start, pg_row;
+	void *buf, *pg_buf = mdb->pg_buf;
+
+	mdb_read_pg(mdb, entry->table_pg);
+	if (mdb_get_byte(pg_buf, 0) != 0x02)  /* not a valid table def page */
+		return NULL;
+	table = mdb_alloc_tabledef(entry);
+
+	len = mdb_get_int16(pg_buf, 8);
+
+	table->num_rows = mdb_get_int32(pg_buf, fmt->tab_num_rows_offset);
+	table->num_var_cols = mdb_get_int16(pg_buf, fmt->tab_num_cols_offset-2);
+	table->num_cols = mdb_get_int16(pg_buf, fmt->tab_num_cols_offset);
+	table->num_idxs = mdb_get_int32(pg_buf, fmt->tab_num_idxs_offset);
+	table->num_real_idxs = mdb_get_int32(pg_buf, fmt->tab_num_ridxs_offset);
+
+	/* grab a copy of the usage map */
+	pg_row = mdb_get_int32(pg_buf, fmt->tab_usage_map_offset);
+	mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->map_sz));
+	table->usage_map = g_memdup(buf + row_start, table->map_sz);
+	if (mdb_get_option(MDB_DEBUG_USAGE)) 
+		buffer_dump(buf, row_start, row_start+table->map_sz-1);
+	mdb_debug(MDB_DEBUG_USAGE,"usage map found on page %ld row %d start %d len %d",
+		pg_row >> 8, pg_row & 0xff, row_start, table->map_sz);
+
+	/* grab a copy of the free space page map */
+	pg_row = mdb_get_int32(pg_buf, fmt->tab_free_map_offset);
+	mdb_find_pg_row(mdb, pg_row, &buf, &row_start, &(table->freemap_sz));
+	table->free_usage_map = g_memdup(buf + row_start, table->freemap_sz);
+	mdb_debug(MDB_DEBUG_USAGE,"free map found on page %ld row %d start %d len %d\n",
+		pg_row >> 8, pg_row & 0xff, row_start, table->freemap_sz);
+
+	table->first_data_pg = mdb_get_int16(pg_buf, fmt->tab_first_dpg_offset);
+
+	return table;
+}
+MdbTableDef *mdb_read_table_by_name(MdbHandle *mdb, gchar *table_name, int obj_type)
+{
+	unsigned int i;
+	MdbCatalogEntry *entry;
+
+	mdb_read_catalog(mdb, obj_type);
+
+	for (i=0; i<mdb->num_catalog; i++) {
+		entry = g_ptr_array_index(mdb->catalog, i);
+		if (!strcasecmp(entry->object_name, table_name))
+			return mdb_read_table(entry);
+	}
+
+	return NULL;
+}
+
+/*
+** read the next page if offset is > pg_size
+** return true if page was read
+*/ 
+int 
+read_pg_if(MdbHandle *mdb, int *cur_pos, int offset)
+{
+	if (*cur_pos + offset >= mdb->fmt->pg_size) {
+		mdb_read_pg(mdb, mdb_get_int32(mdb->pg_buf,4));
+		*cur_pos = 8 - (mdb->fmt->pg_size - (*cur_pos));
+		return 1;
+	}
+	return 0;
+}
+guint32 
+read_pg_if_32(MdbHandle *mdb, int *cur_pos)
+{
+	unsigned char c[4];
+	int i, rc = 0;
+
+	for (i=0;i<4;i++) {
+		rc += read_pg_if(mdb, cur_pos, i);
+		c[i] = mdb->pg_buf[(*cur_pos) + i];
+	}
+	return mdb_get_int32(c, 0);
+}
+guint16 
+read_pg_if_16(MdbHandle *mdb, int *cur_pos)
+{
+	unsigned char low_byte, high_byte;
+	int rc = 0;
+
+	rc += read_pg_if(mdb, cur_pos, 0);
+	low_byte = mdb->pg_buf[*cur_pos];
+	rc += read_pg_if(mdb, cur_pos, 1);
+	high_byte = mdb->pg_buf[(*cur_pos) + 1];
+
+	return (high_byte * 256 + low_byte);
+}
+guint16 
+read_pg_if_n(MdbHandle *mdb, unsigned char *buf, int *cur_pos, int len)
+{
+	if (*cur_pos + len < mdb->fmt->pg_size) {
+		memcpy(buf, &mdb->pg_buf[*cur_pos], len);
+		return 0;
+	} else {
+		int half = mdb->fmt->pg_size - *cur_pos;
+		memcpy(buf, &mdb->pg_buf[*cur_pos], half);
+		mdb_read_pg(mdb, mdb_get_int32(mdb->pg_buf,4));
+		memcpy(buf + half, &mdb->pg_buf[8], len - half);
+		*cur_pos = 8 - half;
+		return 1;
+	}
+}
+
+void mdb_append_column(GPtrArray *columns, MdbColumn *in_col)
+{
+	g_ptr_array_add(columns, g_memdup(in_col,sizeof(MdbColumn)));
+}
+void mdb_free_columns(GPtrArray *columns)
+{
+	unsigned int i;
+
+	if (!columns) return;
+	for (i=0; i<columns->len; i++)
+		g_free (g_ptr_array_index(columns, i));
+	g_ptr_array_free(columns, TRUE);
+}
+GPtrArray *mdb_read_columns(MdbTableDef *table)
+{
+	MdbHandle *mdb = table->entry->mdb;
+	MdbFormatConstants *fmt = mdb->fmt;
+	MdbColumn *pcol;
+	unsigned char *col;
+	unsigned int i;
+	int cur_pos, name_sz;
+	
+	table->columns = g_ptr_array_new();
+
+	col = (unsigned char *) g_malloc(fmt->tab_col_entry_size);
+
+	cur_pos = fmt->tab_cols_start_offset + 
+		(table->num_real_idxs * fmt->tab_ridx_entry_size);
+
+	/* new code based on patch submitted by Tim Nelson 2000.09.27 */
+
+	/* 
+	** column attributes 
+	*/
+	for (i=0;i<table->num_cols;i++) {
+#ifdef MDB_DEBUG
+	/* printf("column %d\n", i);
+	buffer_dump(mdb->pg_buf, cur_pos ,cur_pos + 18); */
+#endif
+		read_pg_if_n(mdb, col, &cur_pos, fmt->tab_col_entry_size);
+		cur_pos += fmt->tab_col_entry_size;
+		pcol = (MdbColumn *) g_malloc0(sizeof(MdbColumn));
+
+		pcol->col_type = col[0];
+
+		// col_num_offset == 1 or 5
+		pcol->col_num = col[fmt->col_num_offset];
+
+		//fprintf(stdout,"----- column %d -----\n",pcol->col_num);
+		// col_var == 3 or 7
+		pcol->var_col_num = mdb_get_int16(col, fmt->tab_col_offset_var);
+		//fprintf(stdout,"var column pos %d\n",pcol->var_col_num);
+
+		// col_var == 5 or 9
+		pcol->row_col_num = mdb_get_int16(col, fmt->tab_row_col_num_offset);
+		//fprintf(stdout,"row column num %d\n",pcol->row_col_num);
+
+		/* FIXME: can this be right in Jet3 and Jet4? */
+		if (pcol->col_type == MDB_NUMERIC) {
+			pcol->col_prec = col[11];
+			pcol->col_scale = col[12];
+		}
+
+		// col_fixed_offset == 13 or 15
+		pcol->is_fixed = col[fmt->col_fixed_offset] & 0x01 ? 1 : 0;
+
+		// col_fixed_offset == 13 or 15
+		pcol->fixed_offset = mdb_get_int16(col, fmt->tab_col_offset_fixed);
+		//fprintf(stdout,"fixed column offset %d\n",pcol->fixed_offset);
+		//fprintf(stdout,"col type %s\n",pcol->is_fixed ? "fixed" : "variable");
+
+		if (pcol->col_type != MDB_BOOL) {
+			// col_size_offset == 16 or 23
+			pcol->col_size = mdb_get_int16(col, fmt->col_size_offset);
+		} else {
+			pcol->col_size=0;
+		}
+		
+		g_ptr_array_add(table->columns, pcol);
+	}
+
+	g_free (col);
+
+	/* 
+	** column names - ordered the same as the column attributes table
+	*/
+	for (i=0;i<table->num_cols;i++) {
+		unsigned char *tmp_buf;
+		pcol = g_ptr_array_index(table->columns, i);
+
+		if (IS_JET4(mdb)) {
+			name_sz = read_pg_if_16(mdb, &cur_pos);
+			cur_pos += 2;
+		} else if (IS_JET3(mdb)) {
+			read_pg_if(mdb, &cur_pos, 0);
+			name_sz = mdb->pg_buf[cur_pos];
+			cur_pos++;
+		} else {
+			fprintf(stderr,"Unknown MDB version\n");
+			continue;
+		}
+		tmp_buf = (unsigned char *) g_malloc(name_sz);
+		read_pg_if_n(mdb, tmp_buf, &cur_pos, name_sz);
+		mdb_unicode2ascii(mdb, tmp_buf, name_sz, pcol->name, MDB_MAX_OBJ_NAME);
+		g_free(tmp_buf);
+		cur_pos += name_sz;
+
+	}
+
+	/* Sort the columns by col_num */
+	g_ptr_array_sort(table->columns, (GCompareFunc)mdb_col_comparer);
+
+	table->index_start = cur_pos;
+	return table->columns;
+}
+
+void mdb_table_dump(MdbCatalogEntry *entry)
+{
+MdbTableDef *table;
+MdbColumn *col;
+int coln;
+MdbIndex *idx;
+MdbHandle *mdb = entry->mdb;
+unsigned int i, bitn;
+guint32 pgnum;
+
+	table = mdb_read_table(entry);
+	fprintf(stdout,"definition page     = %lu\n",entry->table_pg);
+	fprintf(stdout,"number of datarows  = %d\n",table->num_rows);
+	fprintf(stdout,"number of columns   = %d\n",table->num_cols);
+	fprintf(stdout,"number of indices   = %d\n",table->num_real_idxs);
+
+	mdb_read_columns(table);
+	mdb_read_indices(table);
+
+	for (i=0;i<table->num_cols;i++) {
+		col = g_ptr_array_index(table->columns,i);
+	
+		fprintf(stdout,"column %d Name: %-20s Type: %s(%d)\n",
+			i, col->name,
+			mdb_get_coltype_string(mdb->default_backend, col->col_type),
+			col->col_size);
+	}
+
+	for (i=0;i<table->num_idxs;i++) {
+		idx = g_ptr_array_index (table->indices, i);
+		mdb_index_dump(table, idx);
+	}
+	if (table->usage_map) {
+		printf("pages reserved by this object\n");
+		printf("usage map pg %" G_GUINT32_FORMAT "\n",
+			table->map_base_pg);
+		printf("free map pg %" G_GUINT32_FORMAT "\n",
+			table->freemap_base_pg);
+		pgnum = mdb_get_int32(table->usage_map,1);
+		/* the first 5 bytes of the usage map mean something */
+		coln = 0;
+		for (i=5;i<table->map_sz;i++) {
+			for (bitn=0;bitn<8;bitn++) {
+				if (table->usage_map[i] & 1 << bitn) {
+					coln++;
+					printf("%6" G_GUINT32_FORMAT, pgnum);
+					if (coln==10) {
+						printf("\n");
+						coln = 0;
+					} else {
+						printf(" ");
+					}
+				}
+				pgnum++;
+			}
+		}
+		printf("\n");
+	}
+}
+
+int mdb_is_user_table(MdbCatalogEntry *entry)
+{
+	return ((entry->object_type == MDB_TABLE)
+	 && !(entry->flags & 0x80000002)) ? 1 : 0;
+}
+int mdb_is_system_table(MdbCatalogEntry *entry)
+{
+	return ((entry->object_type == MDB_TABLE)
+	 && (entry->flags & 0x80000002)) ? 1 : 0;
+}
diff --git a/providers/mdb/libmdb-src/worktable.c b/providers/mdb/libmdb-src/worktable.c
new file mode 100644
index 0000000..6f893dc
--- /dev/null
+++ b/providers/mdb/libmdb-src/worktable.c
@@ -0,0 +1,99 @@
+/* MDB Tools - A library for reading MS Access database files
+ * Copyright (C) 2004 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "mdbtools.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+/*
+ * Temp table routines.  These are currently used to generate mock results for
+ * commands like "list tables" and "describe table"
+ */
+
+void
+mdb_fill_temp_col(MdbColumn *tcol, char *col_name, int col_size, int col_type, int is_fixed)
+{
+	memset(tcol,0,sizeof(MdbColumn));
+	strcpy(tcol->name, col_name);
+	tcol->col_type = col_type;
+	if ((col_type == MDB_TEXT) || (col_type == MDB_MEMO)) {
+		tcol->col_size = col_size;
+	} else {
+		tcol->col_size = mdb_col_fixed_size(tcol);
+	}
+	tcol->is_fixed = is_fixed;
+}
+void
+mdb_fill_temp_field(MdbField *field, void *value, int siz, int is_fixed, int is_null, int start, int colnum)
+{
+   	field->value = value;
+   	field->siz = siz;
+   	field->is_fixed = is_fixed;
+   	field->is_null = is_null;
+   	field->start = start;
+   	field->colnum = colnum;
+}
+MdbTableDef *
+mdb_create_temp_table(MdbHandle *mdb, char *name)
+{
+	MdbCatalogEntry *entry;
+	MdbTableDef *table;
+
+	/* dummy up a catalog entry */
+	entry = (MdbCatalogEntry *) g_malloc0(sizeof(MdbCatalogEntry));
+	entry->mdb = mdb;
+	entry->object_type = MDB_TABLE;
+	entry->table_pg = 0;
+	strcpy(entry->object_name, name);
+
+	table = mdb_alloc_tabledef(entry);
+	table->columns = g_ptr_array_new();
+	table->is_temp_table = 1;
+	table->temp_table_pages = g_ptr_array_new();
+
+	return table;
+}
+void
+mdb_temp_table_add_col(MdbTableDef *table, MdbColumn *col)
+{
+	col->col_num = table->num_cols;
+	if (!col->is_fixed)
+		col->var_col_num = table->num_var_cols++;
+	g_ptr_array_add(table->columns, g_memdup(col, sizeof(MdbColumn)));
+	table->num_cols++;
+}
+/*
+ * Should be called after setting up all temp table columns
+ */
+void mdb_temp_columns_end(MdbTableDef *table)
+{
+	MdbColumn *col;
+	unsigned int i;
+	unsigned int start = 0;
+
+	for (i=0; i<table->num_cols; i++) {
+		col = g_ptr_array_index(table->columns, i);
+		if (col->is_fixed) {
+			col->fixed_offset = start;
+			start += col->col_size;
+		}
+	}
+}
diff --git a/providers/mdb/libmdb-src/write.c b/providers/mdb/libmdb-src/write.c
new file mode 100644
index 0000000..bfe8b0c
--- /dev/null
+++ b/providers/mdb/libmdb-src/write.c
@@ -0,0 +1,868 @@
+/* MDB Tools - A library for reading MS Access database file
+ * Copyright (C) 2000 Brian Bruns
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "mdbtools.h"
+#include "time.h"
+#include "math.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+
+//static int mdb_copy_index_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg);
+static int mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields, guint32 pgnum, guint16 rownum);
+
+void
+_mdb_put_int16(unsigned char *buf, guint32 offset, guint32 value)
+{
+	buf[offset] = value % 256;
+	value /= 256;
+	buf[offset+1] = value % 256;
+}
+void
+_mdb_put_int32(unsigned char *buf, guint32 offset, guint32 value)
+{
+	buf[offset] = value % 256;
+	value /= 256;
+	buf[offset+1] = value % 256;
+	value /= 256;
+	buf[offset+2] = value % 256;
+	value /= 256;
+	buf[offset+3] = value % 256;
+}
+void
+_mdb_put_int24(unsigned char *buf, guint32 offset, guint32 value)
+{
+	buf[offset] = value % 256;
+	value /= 256;
+	buf[offset+1] = value % 256;
+	value /= 256;
+	buf[offset+2] = value % 256;
+}
+void
+_mdb_put_int24_msb(unsigned char *buf, guint32 offset, guint32 value)
+{
+	buf[offset+2] = value % 256;
+	value /= 256;
+	buf[offset+1] = value % 256;
+	value /= 256;
+	buf[offset] = value % 256;
+}
+ssize_t
+mdb_write_pg(MdbHandle *mdb, unsigned long pg)
+{
+	ssize_t len;
+	struct stat status;
+	off_t offset = pg * mdb->fmt->pg_size;
+
+	fstat(mdb->f->fd, &status);
+	/* is page beyond current size + 1 ? */
+	if (status.st_size < offset + mdb->fmt->pg_size) {
+		fprintf(stderr,"offset %lu is beyond EOF\n",offset);
+		return 0;
+	}
+	lseek(mdb->f->fd, offset, SEEK_SET);
+	len = write(mdb->f->fd,mdb->pg_buf,mdb->fmt->pg_size);
+	if (len==-1) {
+		perror("write");
+		return 0;
+	} else if (len<mdb->fmt->pg_size) {
+	/* fprintf(stderr,"EOF reached %d bytes returned.\n",len, mdb->pg_size); */
+		return 0;
+	}
+	mdb->cur_pos = 0;
+	return len;
+}
+
+static int 
+mdb_is_col_indexed(MdbTableDef *table, int colnum)
+{
+	unsigned int i, j;
+	MdbIndex *idx;
+
+	for (i=0;i<table->num_idxs;i++) {
+		idx = g_ptr_array_index (table->indices, i);
+		for (j=0;j<idx->num_keys;j++) {
+			if (idx->key_col_num[j]==colnum) return 1;
+		}
+	}
+	return 0;
+}
+
+static void
+mdb_crack_row4(MdbHandle *mdb, int row_start, int row_end, unsigned int bitmask_sz, unsigned int row_var_cols, unsigned int *var_col_offsets)
+{
+	unsigned int i;
+
+	for (i=0; i<row_var_cols+1; i++) {
+		var_col_offsets[i] = mdb_get_int16(mdb->pg_buf,
+			row_end - bitmask_sz - 3 - (i*2));
+	}
+}
+static void
+mdb_crack_row3(MdbHandle *mdb, int row_start, int row_end, unsigned int bitmask_sz, unsigned int row_var_cols, unsigned int *var_col_offsets)
+{
+	unsigned int i;
+	unsigned int num_jumps = 0, jumps_used = 0;
+	unsigned int col_ptr, row_len;
+
+	row_len = row_end - row_start + 1;
+	num_jumps = (row_len - 1) / 256;
+	col_ptr = row_end - bitmask_sz - num_jumps - 1;
+	/* If last jump is a dummy value, ignore it */
+	if ((col_ptr-row_start-row_var_cols)/256 < num_jumps)
+		num_jumps--;
+
+	jumps_used = 0;
+	for (i=0; i<row_var_cols+1; i++) {
+		while ((jumps_used < num_jumps)
+		 && (i == mdb->pg_buf[row_end-bitmask_sz-jumps_used-1])) {
+			jumps_used++;
+		}
+		var_col_offsets[i] = mdb->pg_buf[col_ptr-i]+(jumps_used*256);
+	}
+}
+/**
+ * mdb_crack_row:
+ * @table: Table that the row belongs to
+ * @row_start: offset to start of row on current page
+ * @row_end: offset to end of row on current page
+ * @fields: pointer to MdbField array to be popluated by mdb_crack_row
+ *
+ * Cracks a row buffer apart into its component fields.  
+ * 
+ * A row buffer is that portion of a data page which contains the values for
+ * that row.  Its beginning and end can be found in the row offset table.
+ *
+ * The resulting MdbField array contains pointers into the row for each field 
+ * present.  Be aware that by modifying field[]->value, you would be modifying 
+ * the row buffer itself, not a copy.
+ *
+ * This routine is mostly used internally by mdb_fetch_row() but may have some
+ * applicability for advanced application programs.
+ *
+ * Return value: number of fields present.
+ */
+int
+mdb_crack_row(MdbTableDef *table, int row_start, int row_end, MdbField *fields)
+{
+	MdbColumn *col;
+	MdbCatalogEntry *entry = table->entry;
+	MdbHandle *mdb = entry->mdb;
+	unsigned char *pg_buf = mdb->pg_buf;
+	unsigned int row_var_cols=0, row_cols;
+	unsigned char *nullmask;
+	unsigned int bitmask_sz;
+	unsigned int *var_col_offsets = NULL;
+	unsigned int fixed_cols_found, row_fixed_cols;
+	unsigned int col_count_size;
+	unsigned int i;
+
+	if (mdb_get_option(MDB_DEBUG_ROW)) {
+		buffer_dump(pg_buf, row_start, row_end);
+	}
+
+	if (IS_JET4(mdb)) {
+		row_cols = mdb_get_int16(mdb->pg_buf, row_start);
+		col_count_size = 2;
+	} else {
+		row_cols = pg_buf[row_start];
+		col_count_size = 1;
+	}
+
+	bitmask_sz = (row_cols + 7) / 8;
+	nullmask = &pg_buf[row_end - bitmask_sz + 1];
+
+	/* read table of variable column locations */
+	row_var_cols = IS_JET4(mdb) ?
+		mdb_get_int16(mdb->pg_buf, row_end - bitmask_sz - 1) :
+		pg_buf[row_end - bitmask_sz];
+	var_col_offsets = (unsigned int *)g_malloc((row_var_cols+1)*sizeof(int));
+	if (table->num_var_cols > 0) {
+		if (IS_JET4(mdb)) {
+			mdb_crack_row4(mdb, row_start, row_end, bitmask_sz,
+				 row_var_cols, var_col_offsets);
+		} else {
+			mdb_crack_row3(mdb, row_start, row_end, bitmask_sz,
+				 row_var_cols, var_col_offsets);
+		}
+	}
+
+	fixed_cols_found = 0;
+	row_fixed_cols = row_cols - row_var_cols;
+
+	if (mdb_get_option(MDB_DEBUG_ROW)) {
+		fprintf(stdout,"bitmask_sz %d\n", bitmask_sz);
+		fprintf(stdout,"row_var_cols %d\n", row_var_cols);
+		fprintf(stdout,"row_fixed_cols %d\n", row_fixed_cols);
+	}
+
+	for (i=0;i<table->num_cols;i++) {
+		unsigned int byte_num, bit_num;
+		unsigned int col_start;
+		col = g_ptr_array_index(table->columns,i);
+		fields[i].colnum = i;
+		fields[i].is_fixed = (mdb_is_fixed_col(col)) ? 1 : 0;
+		byte_num = col->col_num / 8;
+		bit_num = col->col_num % 8;
+		/* logic on nulls is reverse, 1 is not null, 0 is null */
+		fields[i].is_null = nullmask[byte_num] & (1 << bit_num) ? 0 : 1;
+
+		if ((fields[i].is_fixed)
+		 && (fixed_cols_found < row_fixed_cols)) {
+			col_start = col->fixed_offset + col_count_size;
+			fields[i].start = row_start + col_start;
+			fields[i].value = &pg_buf[row_start + col_start];
+			fields[i].siz = col->col_size;
+			fixed_cols_found++;
+		/* Use col->var_col_num because a deleted column is still
+		 * present in the variable column offsets table for the row */
+		} else if ((!fields[i].is_fixed)
+		 && (col->var_col_num < row_var_cols)) {
+			col_start = var_col_offsets[col->var_col_num];
+			fields[i].start = row_start + col_start;
+			fields[i].value = &pg_buf[row_start + col_start];
+			fields[i].siz = var_col_offsets[(col->var_col_num)+1] -
+		                col_start;
+		} else {
+			fields[i].start = 0;
+			fields[i].value = NULL;
+			fields[i].siz = 0;
+			fields[i].is_null = 1;
+		}
+	}
+
+	g_free(var_col_offsets);
+	return row_cols;
+}
+
+static int
+mdb_pack_null_mask(unsigned char *buffer, int num_fields, MdbField *fields)
+{
+	int pos = 0, bit = 0, byte = 0;
+	int i;
+
+	/* 'Not null' bitmap */
+	for (i=0; i<num_fields; i++) {
+		/* column is null if bit is clear (0) */
+		if (!fields[i].is_null) {
+			byte |= 1 << bit;
+			//printf("%d %d %d %d\n", i, bit, 1 << bit, byte);
+		}
+		bit++;
+		if (bit==8) {
+			buffer[pos++] = byte;
+			bit = byte = 0;
+		}
+	}
+	/* if we've written any bits to the current byte, flush it */
+	if (bit)
+		buffer[pos++] = byte;
+	
+	return pos;
+}
+/* fields must be ordered with fixed columns first, then vars, subsorted by 
+ * column number */
+static int
+mdb_pack_row4(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields)
+{
+	unsigned int pos = 0;
+	unsigned int var_cols = 0;
+	unsigned int i;
+
+	row_buffer[pos++] = num_fields & 0xff;
+	row_buffer[pos++] = (num_fields >> 8) & 0xff; 
+
+	/* Fixed length columns */
+	for (i=0;i<num_fields;i++) {
+		if (fields[i].is_fixed) {
+			fields[i].offset = pos;
+			if (!fields[i].is_null) {
+				memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
+			}
+			pos += fields[i].siz;
+		}
+	}
+	/* For tables without variable-length columns */
+	if (table->num_var_cols == 0) {
+		pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
+		return pos;
+	}
+	/* Variable length columns */
+	for (i=0;i<num_fields;i++) {
+		if (!fields[i].is_fixed) {
+			var_cols++;
+			fields[i].offset = pos;
+			if (! fields[i].is_null) {
+				memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
+				pos += fields[i].siz;
+			}
+		}
+	}
+	/* EOD */
+	row_buffer[pos] = pos & 0xff;
+	row_buffer[pos+1] = (pos >> 8) & 0xff;
+	pos += 2;
+
+	/* Offsets of the variable-length columns */
+	for (i=num_fields; i>0; i--) {
+		if (!fields[i-1].is_fixed) {
+			row_buffer[pos++] = fields[i-1].offset & 0xff;
+			row_buffer[pos++] = (fields[i-1].offset >> 8) & 0xff;
+		}
+	}
+	/* Number of variable-length columns */
+	row_buffer[pos++] = var_cols & 0xff;
+	row_buffer[pos++] = (var_cols >> 8) & 0xff;
+
+	pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
+	return pos;
+}
+
+static int
+mdb_pack_row3(MdbTableDef *table, unsigned char *row_buffer, unsigned int num_fields, MdbField *fields)
+{
+	unsigned int pos = 0;
+	unsigned int var_cols = 0;
+	unsigned int i, j;
+	unsigned char *offset_high;
+
+	row_buffer[pos++] = num_fields;
+
+	/* Fixed length columns */
+	for (i=0;i<num_fields;i++) {
+		if (fields[i].is_fixed) {
+			fields[i].offset = pos;
+			if (!fields[i].is_null) {
+				memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
+			}
+			pos += fields[i].siz;
+		}
+	}
+	/* For tables without variable-length columns */
+	if (table->num_var_cols == 0) {
+		pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
+		return pos;
+	}
+	/* Variable length columns */
+	for (i=0;i<num_fields;i++) {
+		if (!fields[i].is_fixed) {
+			var_cols++;
+			fields[i].offset = pos;
+			if (! fields[i].is_null) {
+				memcpy(&row_buffer[pos], fields[i].value, fields[i].siz);
+				pos += fields[i].siz;
+			}
+		}
+	}
+
+	offset_high = (unsigned char *) g_malloc(var_cols+1);
+	offset_high[0] = (pos << 8) & 0xff;
+	j = 1;
+	
+	/* EOD */
+	row_buffer[pos] = pos & 0xff;
+	pos++;
+
+	/* Variable length column offsets */
+	for (i=num_fields; i>0; i--) {
+		if (!fields[i-1].is_fixed) {
+			row_buffer[pos++] = fields[i-1].offset & 0xff;
+			offset_high[j++] = (fields[i-1].offset << 8) & 0xff;
+		}
+	}
+
+	/* Dummy jump table entry */
+	if (offset_high[0] < (pos+(num_fields+7)/8-1)/255) {
+		row_buffer[pos++] = 0xff;
+	}
+	/* Jump table */
+	for (i=0; i<var_cols; i++) {
+		if (offset_high[i] > offset_high[i+1]) {
+			row_buffer[pos++] = var_cols-i;
+		}
+	}
+	g_free(offset_high);
+
+	row_buffer[pos++] = var_cols;
+
+	pos += mdb_pack_null_mask(&row_buffer[pos], num_fields, fields);
+	return pos;
+}
+int
+mdb_pack_row(MdbTableDef *table, unsigned char *row_buffer, int unsigned num_fields, MdbField *fields)
+{
+	if (table->is_temp_table) {
+		unsigned int i;
+		for (i=0; i<num_fields; i++) {
+			MdbColumn *c = g_ptr_array_index(table->columns, i);
+			fields[i].is_null = (fields[i].value) ? 0 : 1;
+			fields[i].colnum = i;
+			fields[i].is_fixed = c->is_fixed;
+			if ((c->col_type != MDB_TEXT)
+			 && (c->col_type != MDB_MEMO)) {
+				fields[i].siz = c->col_size;
+			}
+		}
+	}
+	if (IS_JET4(table->entry->mdb)) {
+		return mdb_pack_row4(table, row_buffer, num_fields, fields);
+	} else {
+		return mdb_pack_row3(table, row_buffer, num_fields, fields);
+	}
+}
+int
+mdb_pg_get_freespace(MdbHandle *mdb)
+{
+	int rows, free_start, free_end;
+	int row_count_offset = mdb->fmt->row_count_offset;
+
+	rows = mdb_get_int16(mdb->pg_buf, row_count_offset);
+	free_start = row_count_offset + 2 + (rows * 2);
+	free_end = mdb_get_int16(mdb->pg_buf, row_count_offset + (rows * 2));
+	mdb_debug(MDB_DEBUG_WRITE,"free space left on page = %d", free_end - free_start);
+	return (free_end - free_start);
+}
+unsigned char *
+mdb_new_leaf_pg(MdbCatalogEntry *entry)
+{
+	MdbHandle *mdb = entry->mdb;
+	unsigned char *new_pg;
+
+	new_pg = (unsigned char *) g_malloc0(mdb->fmt->pg_size);
+		
+	new_pg[0]=0x04;
+	new_pg[1]=0x01;
+	_mdb_put_int32(new_pg, 4, entry->table_pg);
+	
+	return new_pg;
+}
+unsigned char *
+mdb_new_data_pg(MdbCatalogEntry *entry)
+{
+	MdbFormatConstants *fmt = entry->mdb->fmt;
+	unsigned char *new_pg;
+
+	new_pg = (unsigned char *) g_malloc0(fmt->pg_size);
+		
+	new_pg[0]=0x01;
+	new_pg[1]=0x01;
+	_mdb_put_int16(new_pg, 2, fmt->pg_size - fmt->row_count_offset - 2);
+	_mdb_put_int32(new_pg, 4, entry->table_pg);
+	
+	return new_pg;
+}
+
+int
+mdb_update_indexes(MdbTableDef *table, int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum)
+{
+	unsigned int i;
+	MdbIndex *idx;
+	
+	for (i=0;i<table->num_idxs;i++) {
+		idx = g_ptr_array_index (table->indices, i);
+		mdb_debug(MDB_DEBUG_WRITE,"Updating %s (%d).", idx->name, idx->index_type);
+		if (idx->index_type==1) {
+			mdb_update_index(table, idx, num_fields, fields, pgnum, rownum);
+		}
+	}
+	return 1;
+}
+
+int
+mdb_init_index_chain(MdbTableDef *table, MdbIndex *idx)
+{
+	MdbCatalogEntry *entry = table->entry;
+	MdbHandle *mdb = entry->mdb;
+
+	table->scan_idx = idx;
+	table->chain = g_malloc0(sizeof(MdbIndexChain));
+	table->mdbidx = mdb_clone_handle(mdb);
+	mdb_read_pg(table->mdbidx, table->scan_idx->first_pg);
+
+	return 1;
+}
+
+int
+mdb_update_index(MdbTableDef *table, MdbIndex *idx, unsigned int num_fields, MdbField *fields, guint32 pgnum, guint16 rownum)
+{
+	MdbCatalogEntry *entry = table->entry;
+	MdbHandle *mdb = entry->mdb;
+	int idx_xref[16];
+	unsigned int i, j;
+	MdbIndexChain *chain;
+	MdbField idx_fields[10];
+
+	for (i = 0; i < idx->num_keys; i++) {
+		for (j = 0; j < num_fields; j++) {
+			// key_col_num is 1 based, can't remember why though
+			if (fields[j].colnum == idx->key_col_num[i]-1) {
+				idx_xref[i] = j;
+				idx_fields[i] = fields[j];
+			}
+		}
+	}
+/*
+	for (i = 0; i < idx->num_keys; i++) {
+		fprintf(stdout, "key col %d (%d) is mapped to field %d (%d %d)\n",
+			i, idx->key_col_num[i], idx_xref[i], fields[idx_xref[i]].colnum, 
+			fields[idx_xref[i]].siz);
+	}
+	for (i = 0; i < num_fields; i++) {
+		fprintf(stdout, "%d (%d %d)\n",
+			i, fields[i].colnum, 
+			fields[i].siz);
+	}
+*/
+
+	chain = g_malloc0(sizeof(MdbIndexChain));
+
+	mdb_index_find_row(mdb, idx, chain, pgnum, rownum);
+	//printf("chain depth = %d\n", chain->cur_depth);
+	//printf("pg = %" G_GUINT32_FORMAT "\n",
+		//chain->pages[chain->cur_depth-1].pg);
+	//mdb_copy_index_pg(table, idx, &chain->pages[chain->cur_depth-1]);
+	mdb_add_row_to_leaf_pg(table, idx, &chain->pages[chain->cur_depth-1], idx_fields, pgnum, rownum);
+	
+	return 1;
+}
+
+int
+mdb_insert_row(MdbTableDef *table, int num_fields, MdbField *fields)
+{
+	int new_row_size;
+	unsigned char row_buffer[4096];
+	MdbCatalogEntry *entry = table->entry;
+	MdbHandle *mdb = entry->mdb;
+	MdbFormatConstants *fmt = mdb->fmt;
+	guint32 pgnum;
+	guint16 rownum;
+
+	if (!mdb->f->writable) {
+		fprintf(stderr, "File is not open for writing\n");
+		return 0;
+	}
+	new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields);
+	if (mdb_get_option(MDB_DEBUG_WRITE)) {
+		buffer_dump(row_buffer, 0, new_row_size-1);
+	}
+	pgnum = mdb_map_find_next_freepage(table, new_row_size);
+	if (!pgnum) {
+		fprintf(stderr, "Unable to allocate new page.\n");
+		return 0;
+	}
+
+	rownum = mdb_add_row_to_pg(table, row_buffer, new_row_size);
+
+	if (mdb_get_option(MDB_DEBUG_WRITE)) {
+		buffer_dump(mdb->pg_buf, 0, 39);
+		buffer_dump(mdb->pg_buf, fmt->pg_size - 160, fmt->pg_size-1);
+	}
+	mdb_debug(MDB_DEBUG_WRITE, "writing page %d", pgnum);
+	if (!mdb_write_pg(mdb, pgnum)) {
+		fprintf(stderr, "write failed! exiting...\n");
+		exit(1);
+	}
+
+	mdb_update_indexes(table, num_fields, fields, pgnum, rownum);
+ 
+	return 1;
+}
+/*
+ * Assumes caller has verfied space is available on page and adds the new 
+ * row to the current pg_buf.
+ */
+guint16
+mdb_add_row_to_pg(MdbTableDef *table, unsigned char *row_buffer, int new_row_size)
+{
+	unsigned char *new_pg;
+	int num_rows, i, pos, row_start, row_size;
+	MdbCatalogEntry *entry = table->entry;
+	MdbHandle *mdb = entry->mdb;
+	MdbFormatConstants *fmt = mdb->fmt;
+
+	if (table->is_temp_table) {
+		GPtrArray *pages = table->temp_table_pages;
+		if (pages->len == 0) {
+			new_pg = mdb_new_data_pg(entry);
+			g_ptr_array_add(pages, new_pg);
+		} else {
+			new_pg = g_ptr_array_index(pages, pages->len - 1);
+			if (mdb_get_int16(new_pg, 2) < new_row_size + 2) {
+				new_pg = mdb_new_data_pg(entry);
+				g_ptr_array_add(pages, new_pg);
+			}
+		}
+
+		num_rows = mdb_get_int16(new_pg, fmt->row_count_offset);
+		pos = (num_rows == 0) ? fmt->pg_size :
+			mdb_get_int16(new_pg, fmt->row_count_offset + (num_rows*2));
+	} else {  /* is not a temp table */
+		new_pg = mdb_new_data_pg(entry);
+
+		num_rows = mdb_get_int16(mdb->pg_buf, fmt->row_count_offset);
+		pos = fmt->pg_size;
+
+		/* copy existing rows */
+		for (i=0;i<num_rows;i++) {
+			mdb_find_row(mdb, i, &row_start, &row_size);
+			pos -= row_size;
+			memcpy(&new_pg[pos], &mdb->pg_buf[row_start], row_size);
+			_mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (i*2), pos);
+		}
+	}
+
+	/* add our new row */
+	pos -= new_row_size;
+	memcpy(&new_pg[pos], row_buffer, new_row_size);
+	/* add row to the row offset table */
+	_mdb_put_int16(new_pg, (fmt->row_count_offset + 2) + (num_rows*2), pos);
+
+	/* update number rows on this page */
+	num_rows++;
+	_mdb_put_int16(new_pg, fmt->row_count_offset, num_rows);
+
+	/* update the freespace */
+	_mdb_put_int16(new_pg,2,pos - fmt->row_count_offset - 2 - (num_rows*2));
+
+	/* copy new page over old */
+	if (!table->is_temp_table) {
+		memcpy(mdb->pg_buf, new_pg, fmt->pg_size);
+		g_free(new_pg);
+	}
+
+	return num_rows;
+}
+int 
+mdb_update_row(MdbTableDef *table)
+{
+int row_start, row_end;
+unsigned int i;
+MdbColumn *col;
+MdbCatalogEntry *entry = table->entry;
+MdbHandle *mdb = entry->mdb;
+MdbField fields[256];
+unsigned char row_buffer[4096];
+int old_row_size, new_row_size, delta;
+unsigned int num_fields;
+
+	if (!mdb->f->writable) {
+		fprintf(stderr, "File is not open for writing\n");
+		return 0;
+	}
+	mdb_find_row(mdb, table->cur_row-1, &row_start, &old_row_size);
+	row_end = row_start + old_row_size - 1;
+
+	row_start &= 0x0FFF; /* remove flags */
+
+	mdb_debug(MDB_DEBUG_WRITE,"page %lu row %d start %d end %d", (unsigned long) table->cur_phys_pg, table->cur_row-1, row_start, row_end);
+	if (mdb_get_option(MDB_DEBUG_LIKE))
+		buffer_dump(mdb->pg_buf, row_start, row_end);
+
+	for (i=0;i<table->num_cols;i++) {
+		col = g_ptr_array_index(table->columns,i);
+		if (col->bind_ptr && mdb_is_col_indexed(table,i)) {
+			fprintf(stderr, "Attempting to update column that is part of an index\n");
+			return 0;
+		}
+	}
+	num_fields = mdb_crack_row(table, row_start, row_end, fields);
+
+	if (mdb_get_option(MDB_DEBUG_WRITE)) {
+		for (i=0;i<num_fields;i++) {
+			//printf("col %d %d start %d siz %d fixed 5d\n", i, fields[i].colnum, fields[i].start, fields[i].siz, fields[i].is_fixed);
+		}
+	}
+	for (i=0;i<table->num_cols;i++) {
+		col = g_ptr_array_index(table->columns,i);
+		if (col->bind_ptr) {
+			fields[i].value = col->bind_ptr;
+			fields[i].siz = *(col->len_ptr);
+		}
+	}
+
+	new_row_size = mdb_pack_row(table, row_buffer, num_fields, fields);
+	if (mdb_get_option(MDB_DEBUG_WRITE)) 
+		buffer_dump(row_buffer, 0, new_row_size-1);
+	delta = new_row_size - old_row_size;
+	if ((mdb_pg_get_freespace(mdb) - delta) < 0) {
+		fprintf(stderr, "No space left on this page, update will not occur\n");
+		return 0;
+	}
+	/* do it! */
+	mdb_replace_row(table, table->cur_row-1, row_buffer, new_row_size);
+	return 0;
+}
+int 
+mdb_replace_row(MdbTableDef *table, int row, unsigned char *new_row, int new_row_size)
+{
+MdbCatalogEntry *entry = table->entry;
+MdbHandle *mdb = entry->mdb;
+int pg_size = mdb->fmt->pg_size;
+int rco = mdb->fmt->row_count_offset;
+unsigned char *new_pg;
+guint16 num_rows;
+int row_start, row_size;
+int i, pos;
+
+	if (mdb_get_option(MDB_DEBUG_WRITE)) {
+		buffer_dump(mdb->pg_buf, 0, 39);
+		buffer_dump(mdb->pg_buf, pg_size - 160, pg_size-1);
+	}
+	mdb_debug(MDB_DEBUG_WRITE,"updating row %d on page %lu", row, (unsigned long) table->cur_phys_pg);
+	new_pg = mdb_new_data_pg(entry);
+
+	num_rows = mdb_get_int16(mdb->pg_buf, rco);
+	_mdb_put_int16(new_pg, rco, num_rows);
+
+	pos = pg_size;
+
+	/* rows before */
+	for (i=0;i<row;i++) {
+		mdb_find_row(mdb, i, &row_start, &row_size);
+		pos -= row_size;
+		memcpy(&new_pg[pos], &mdb->pg_buf[row_start], row_size);
+		_mdb_put_int16(new_pg, rco + 2 + i*2, pos);
+	}
+	
+	/* our row */
+	pos -= new_row_size;
+	memcpy(&new_pg[pos], new_row, new_row_size);
+	_mdb_put_int16(new_pg, rco + 2 + row*2, pos);
+	
+	/* rows after */
+	for (i=row+1;i<num_rows;i++) {
+		mdb_find_row(mdb, i, &row_start, &row_size);
+		pos -= row_size;
+		memcpy(&new_pg[pos], &mdb->pg_buf[row_start], row_size);
+		_mdb_put_int16(new_pg, rco + 2 + i*2, pos);
+	}
+
+	/* almost done, copy page over current */
+	memcpy(mdb->pg_buf, new_pg, pg_size);
+
+	g_free(new_pg);
+
+	_mdb_put_int16(mdb->pg_buf, 2, mdb_pg_get_freespace(mdb));
+	if (mdb_get_option(MDB_DEBUG_WRITE)) {
+		buffer_dump(mdb->pg_buf, 0, 39);
+		buffer_dump(mdb->pg_buf, pg_size - 160, pg_size-1);
+	}
+	/* drum roll, please */
+	if (!mdb_write_pg(mdb, table->cur_phys_pg)) {
+		fprintf(stderr, "write failed! exiting...\n");
+		exit(1);
+	}
+	return 0;
+}
+static int
+mdb_add_row_to_leaf_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg, MdbField *idx_fields, guint32 pgnum, guint16 rownum) 
+/*,  guint32 pgnum, guint16 rownum) 
+static int
+mdb_copy_index_pg(MdbTableDef *table, MdbIndex *idx, MdbIndexPage *ipg)
+*/
+{
+	MdbCatalogEntry *entry = table->entry;
+	MdbHandle *mdb = entry->mdb;
+	MdbColumn *col;
+	guint32 pg;
+	guint16 row;
+	unsigned char *new_pg;
+	unsigned char key_hash[256];
+	unsigned char iflag;
+	int keycol;
+
+	new_pg = mdb_new_leaf_pg(entry);
+
+	/* reinitial ipg pointers to start of page */
+	mdb_index_page_reset(ipg);
+	mdb_read_pg(mdb, ipg->pg);
+
+	/* do we support this index type yet? */
+	if (idx->num_keys > 1) {
+		fprintf(stderr,"multikey indexes not yet supported, aborting\n");
+		return 0;
+	}
+	keycol = idx->key_col_num[0];
+	col = g_ptr_array_index (table->columns, keycol - 1);
+	if (!mdb_is_fixed_col(col)) {
+		fprintf(stderr,"variable length key columns not yet supported, aborting\n");
+		return 0;
+	}
+
+	while (mdb_index_find_next_on_page(mdb, ipg)) {
+
+		/* check for compressed indexes.  */
+		if (ipg->len < col->col_size + 1) {
+			fprintf(stderr,"compressed indexes not yet supported, aborting\n");
+			return 0;
+		}
+
+		pg = mdb_pg_get_int24_msb(mdb, ipg->offset + ipg->len - 4);
+		row = mdb->pg_buf[ipg->offset + ipg->len - 1];
+		iflag = mdb->pg_buf[ipg->offset];
+
+		/* turn the key hash back into a value */
+		mdb_index_swap_n(&mdb->pg_buf[ipg->offset + 1], col->col_size, key_hash);
+		key_hash[col->col_size - 1] &= 0x7f;
+
+		if (mdb_get_option(MDB_DEBUG_WRITE)) {
+			buffer_dump(mdb->pg_buf, ipg->offset, ipg->offset + ipg->len - 1);
+			buffer_dump(mdb->pg_buf, ipg->offset + 1, ipg->offset + col->col_size);
+			buffer_dump(key_hash, 0, col->col_size - 1);
+		}
+
+		memcpy(&new_pg[ipg->offset], &mdb->pg_buf[ipg->offset], ipg->len);
+		ipg->offset += ipg->len;
+		ipg->len = 0;
+
+		row++;
+	}
+	//_mdb_put_int16(new_pg, mdb->fmt->row_count_offset, row);
+	/* free space left */
+	_mdb_put_int16(new_pg, 2, mdb->fmt->pg_size - ipg->offset);
+	//printf("offset = %d\n", ipg->offset);
+
+	mdb_index_swap_n(idx_fields[0].value, col->col_size, key_hash);
+	key_hash[0] |= 0x080;
+	if (mdb_get_option(MDB_DEBUG_WRITE)) {
+		printf("key_hash\n");
+		buffer_dump(idx_fields[0].value, 0, col->col_size-1);
+		buffer_dump(key_hash, 0, col->col_size-1);
+		printf("--------\n");
+	}
+	new_pg[ipg->offset] = 0x7f;
+	memcpy(&new_pg[ipg->offset + 1], key_hash, col->col_size);
+	_mdb_put_int24_msb(new_pg, ipg->offset + 5, pgnum);
+	new_pg[ipg->offset + 8] = rownum-1;
+	ipg->idx_starts[row++] = ipg->offset + ipg->len;
+	//ipg->idx_starts[row] = ipg->offset + ipg->len;
+	if (mdb_get_option(MDB_DEBUG_WRITE)) {
+		buffer_dump(mdb->pg_buf, 0, mdb->fmt->pg_size-1);
+	}
+	memcpy(mdb->pg_buf, new_pg, mdb->fmt->pg_size);
+	mdb_index_pack_bitmap(mdb, ipg);
+	if (mdb_get_option(MDB_DEBUG_WRITE)) {
+		buffer_dump(mdb->pg_buf, 0, mdb->fmt->pg_size-1);
+	}
+	g_free(new_pg);
+
+	return ipg->len;
+}



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]