[libgda/gtk3] MSAccess provider: added an embedded version of MDBTools' code for Windows build
- From: Vivien Malerba <vivien src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libgda/gtk3] MSAccess provider: added an embedded version of MDBTools' code for Windows build
- Date: Tue, 22 Feb 2011 20:43:47 +0000 (UTC)
commit c9ad4b58be6f97ff651914c82b50b9963abf14a2
Author: Vivien Malerba <malerba gnome-db org>
Date: Tue Feb 22 21:42:20 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 5c0b54f..b58c3ce 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]]),
@@ -801,6 +809,7 @@ providers/bdb/libgda-bdb-5.0.pc
providers/bdbsql/Makefile
providers/bdbsql/libgda-bdbsql-5.0.pc
providers/mdb/Makefile
+providers/mdb/libmdb-src/Makefile
providers/mdb/libgda-mdb-5.0.pc
providers/mysql/Makefile
providers/mysql/libgda-mysql-5.0.pc
diff --git a/installers/Windows/make-zip-setup.sh b/installers/Windows/make-zip-setup.sh
index efa716b..2241b60 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 b5368b7..2bc0946 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-5.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-5.0.la \
$(LIBGDA_LIBS) \
- $(MDB_LIBS)
+ $(extra_mdb_ldflags)
xmldir = $(datadir)/libgda-5.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]