diff --git a/build/top.mk b/build/top.mk
index 02d343091852ee280b7568668ea847b86bdc9980..468dd5e482238be88fe7d961ab9a00c5b618d7a1 100644
--- a/build/top.mk
+++ b/build/top.mk
@@ -159,6 +159,7 @@ LUTIL_LIBS = @LUTIL_LIBS@
 LTHREAD_LIBS = @LTHREAD_LIBS@
 
 BDB_LIBS = @BDB_LIBS@
+SLAPD_NDB_LIBS = @SLAPD_NDB_LIBS@
 
 LDAP_LIBLBER_LA = $(LDAP_LIBDIR)/liblber/liblber.la
 LDAP_LIBLDAP_LA = $(LDAP_LIBDIR)/libldap/libldap.la
diff --git a/configure b/configure
index c722177a1429a81d31de4612e3822afaf2169672..8ddc7e9aa44335770bed79e8e9ce4f4e182686bc 100755
--- a/configure
+++ b/configure
@@ -1,5 +1,5 @@
 #! /bin/sh
-# From configure.in OpenLDAP: pkg/ldap/configure.in,v 1.631.2.9 2008/02/11 23:26:37 kurt Exp .
+# From configure.in OpenLDAP: pkg/ldap/configure.in,v 1.631.2.10 2008/09/02 22:34:18 quanah Exp .
 # Guess values for system-dependent variables and create Makefiles.
 # Generated by GNU Autoconf 2.59.
 #
@@ -465,7 +465,7 @@ ac_includes_default="\
 # include <unistd.h>
 #endif"
 
-ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar OPENLDAP_LIBRELEASE OPENLDAP_LIBVERSION OPENLDAP_RELEASE_DATE top_builddir ldap_subdir CC AR CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE EGREP LN_S ECHO ac_ct_AR RANLIB ac_ct_RANLIB DLLTOOL ac_ct_DLLTOOL AS ac_ct_AS OBJDUMP ac_ct_OBJDUMP CPP LIBTOOL PERLBIN OL_MKDEP OL_MKDEP_FLAGS LTSTATIC LIBOBJS LIBSRCS PLAT WITH_SASL WITH_TLS WITH_MODULES_ENABLED WITH_ACI_ENABLED BUILD_THREAD BUILD_LIBS_DYNAMIC BUILD_SLAPD BUILD_SLAPI SLAPD_SLAPI_DEPEND BUILD_BDB BUILD_DNSSRV BUILD_HDB BUILD_LDAP BUILD_META BUILD_MONITOR BUILD_NULL BUILD_PASSWD BUILD_RELAY BUILD_PERL BUILD_SHELL BUILD_SOCK BUILD_SQL BUILD_ACCESSLOG BUILD_AUDITLOG BUILD_CONSTRAINT BUILD_DDS BUILD_DENYOP BUILD_DYNGROUP BUILD_DYNLIST BUILD_LASTMOD BUILD_MEMBEROF BUILD_PPOLICY BUILD_PROXYCACHE BUILD_REFINT BUILD_RETCODE BUILD_RWM BUILD_SEQMOD BUILD_SYNCPROV BUILD_TRANSLUCENT BUILD_UNIQUE BUILD_VALSORT LDAP_LIBS SLAPD_LIBS BDB_LIBS LTHREAD_LIBS LUTIL_LIBS WRAP_LIBS SLAPD_MODULES_CPPFLAGS SLAPD_MODULES_LDFLAGS SLAPD_NO_STATIC SLAPD_STATIC_BACKENDS SLAPD_DYNAMIC_BACKENDS SLAPD_STATIC_OVERLAYS SLAPD_DYNAMIC_OVERLAYS PERL_CPPFLAGS SLAPD_PERL_LDFLAGS MOD_PERL_LDFLAGS KRB4_LIBS KRB5_LIBS SASL_LIBS TLS_LIBS MODULES_LIBS SLAPI_LIBS LIBSLAPI LIBSLAPITOOLS AUTH_LIBS ICU_LIBS SLAPD_SLP_LIBS SLAPD_GMP_LIBS SLAPD_SQL_LDFLAGS SLAPD_SQL_LIBS SLAPD_SQL_INCLUDES LTLIBOBJS'
+ac_subst_vars='SHELL PATH_SEPARATOR PACKAGE_NAME PACKAGE_TARNAME PACKAGE_VERSION PACKAGE_STRING PACKAGE_BUGREPORT exec_prefix prefix program_transform_name bindir sbindir libexecdir datadir sysconfdir sharedstatedir localstatedir libdir includedir oldincludedir infodir mandir build_alias host_alias target_alias DEFS ECHO_C ECHO_N ECHO_T LIBS build build_cpu build_vendor build_os host host_cpu host_vendor host_os target target_cpu target_vendor target_os INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA CYGPATH_W PACKAGE VERSION ACLOCAL AUTOCONF AUTOMAKE AUTOHEADER MAKEINFO install_sh STRIP ac_ct_STRIP INSTALL_STRIP_PROGRAM mkdir_p AWK SET_MAKE am__leading_dot AMTAR am__tar am__untar OPENLDAP_LIBRELEASE OPENLDAP_LIBVERSION OPENLDAP_RELEASE_DATE top_builddir ldap_subdir CC AR CFLAGS LDFLAGS CPPFLAGS ac_ct_CC EXEEXT OBJEXT DEPDIR am__include am__quote AMDEP_TRUE AMDEP_FALSE AMDEPBACKSLASH CCDEPMODE am__fastdepCC_TRUE am__fastdepCC_FALSE EGREP LN_S ECHO ac_ct_AR RANLIB ac_ct_RANLIB DLLTOOL ac_ct_DLLTOOL AS ac_ct_AS OBJDUMP ac_ct_OBJDUMP CPP LIBTOOL PERLBIN OL_MKDEP OL_MKDEP_FLAGS LTSTATIC MYSQL LIBOBJS LIBSRCS PLAT WITH_SASL WITH_TLS WITH_MODULES_ENABLED WITH_ACI_ENABLED BUILD_THREAD BUILD_LIBS_DYNAMIC BUILD_SLAPD BUILD_SLAPI SLAPD_SLAPI_DEPEND BUILD_BDB BUILD_DNSSRV BUILD_HDB BUILD_LDAP BUILD_META BUILD_MONITOR BUILD_NDB BUILD_NULL BUILD_PASSWD BUILD_RELAY BUILD_PERL BUILD_SHELL BUILD_SOCK BUILD_SQL BUILD_ACCESSLOG BUILD_AUDITLOG BUILD_CONSTRAINT BUILD_DDS BUILD_DENYOP BUILD_DYNGROUP BUILD_DYNLIST BUILD_LASTMOD BUILD_MEMBEROF BUILD_PPOLICY BUILD_PROXYCACHE BUILD_REFINT BUILD_RETCODE BUILD_RWM BUILD_SEQMOD BUILD_SYNCPROV BUILD_TRANSLUCENT BUILD_UNIQUE BUILD_VALSORT LDAP_LIBS SLAPD_LIBS BDB_LIBS SLAPD_NDB_LIBS SLAPD_NDB_INCS LTHREAD_LIBS LUTIL_LIBS WRAP_LIBS SLAPD_MODULES_CPPFLAGS SLAPD_MODULES_LDFLAGS SLAPD_NO_STATIC SLAPD_STATIC_BACKENDS SLAPD_DYNAMIC_BACKENDS SLAPD_STATIC_OVERLAYS SLAPD_DYNAMIC_OVERLAYS PERL_CPPFLAGS SLAPD_PERL_LDFLAGS MOD_PERL_LDFLAGS KRB4_LIBS KRB5_LIBS SASL_LIBS TLS_LIBS MODULES_LIBS SLAPI_LIBS LIBSLAPI LIBSLAPITOOLS AUTH_LIBS ICU_LIBS SLAPD_SLP_LIBS SLAPD_GMP_LIBS SLAPD_SQL_LDFLAGS SLAPD_SQL_LIBS SLAPD_SQL_INCLUDES LTLIBOBJS'
 ac_subst_files=''
 
 # Initialize some variables set by options.
@@ -1036,6 +1036,7 @@ SLAPD Backend Options:
     --enable-ldap	  enable ldap backend no|yes|mod [no]
     --enable-meta	  enable metadirectory backend no|yes|mod [no]
     --enable-monitor	  enable monitor backend no|yes|mod [yes]
+    --enable-ndb	  enable MySQL NDB Cluster backend no|yes|mod [no]
     --enable-null	  enable null backend no|yes|mod [no]
     --enable-passwd	  enable passwd backend no|yes|mod [no]
     --enable-perl	  enable perl backend no|yes|mod [no]
@@ -2784,6 +2785,7 @@ Backends="bdb \
 	ldap \
 	meta \
 	monitor \
+	ndb \
 	null \
 	passwd \
 	perl \
@@ -2957,6 +2959,29 @@ else
   	ol_enable_monitor=${ol_enable_backends:-yes}
 fi;
 # end --enable-monitor
+# OpenLDAP --enable-ndb
+
+	# Check whether --enable-ndb or --disable-ndb was given.
+if test "${enable_ndb+set}" = set; then
+  enableval="$enable_ndb"
+
+	ol_arg=invalid
+	for ol_val in no yes mod ; do
+		if test "$enableval" = "$ol_val" ; then
+			ol_arg="$ol_val"
+		fi
+	done
+	if test "$ol_arg" = "invalid" ; then
+		{ { echo "$as_me:$LINENO: error: bad value $enableval for --enable-ndb" >&5
+echo "$as_me: error: bad value $enableval for --enable-ndb" >&2;}
+   { (exit 1); exit 1; }; }
+	fi
+	ol_enable_ndb="$ol_arg"
+
+else
+  	ol_enable_ndb=${ol_enable_backends:-no}
+fi;
+# end --enable-ndb
 # OpenLDAP --enable-null
 
 	# Check whether --enable-null or --disable-null was given.
@@ -3703,6 +3728,7 @@ elif test $ol_enable_modules != yes &&
 	test $ol_enable_ldap = no &&
 	test $ol_enable_meta = no &&
 	test $ol_enable_monitor = no &&
+	test $ol_enable_ndb = no &&
 	test $ol_enable_null = no &&
 	test $ol_enable_passwd = no &&
 	test $ol_enable_perl = no &&
@@ -3750,6 +3776,8 @@ echo "${ECHO_T}done" >&6
 
 LDAP_LIBS=
 BDB_LIBS=
+SLAPD_NDB_LIBS=
+SLAPD_NDB_INCS=
 LTHREAD_LIBS=
 LUTIL_LIBS=
 
@@ -3768,6 +3796,7 @@ BUILD_HDB=no
 BUILD_LDAP=no
 BUILD_META=no
 BUILD_MONITOR=no
+BUILD_NDB=no
 BUILD_NULL=no
 BUILD_PASSWD=no
 BUILD_PERL=no
@@ -5621,7 +5650,7 @@ ia64-*-hpux*)
   ;;
 *-*-irix6*)
   # Find out which ABI we are using.
-  echo '#line 5624 "configure"' > conftest.$ac_ext
+  echo '#line 5653 "configure"' > conftest.$ac_ext
   if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
   (eval $ac_compile) 2>&5
   ac_status=$?
@@ -7601,11 +7630,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7604: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7633: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:7608: \$? = $ac_status" >&5
+   echo "$as_me:7637: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -7863,11 +7892,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7866: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7895: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>conftest.err)
    ac_status=$?
    cat conftest.err >&5
-   echo "$as_me:7870: \$? = $ac_status" >&5
+   echo "$as_me:7899: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s "$ac_outfile"; then
      # The compiler can only warn and ignore the option if not recognized
      # So say no if there are warnings other than the usual output.
@@ -7925,11 +7954,11 @@ else
    -e 's:.*FLAGS}\{0,1\} :&$lt_compiler_flag :; t' \
    -e 's: [^ ]*conftest\.: $lt_compiler_flag&:; t' \
    -e 's:$: $lt_compiler_flag:'`
-   (eval echo "\"\$as_me:7928: $lt_compile\"" >&5)
+   (eval echo "\"\$as_me:7957: $lt_compile\"" >&5)
    (eval "$lt_compile" 2>out/conftest.err)
    ac_status=$?
    cat out/conftest.err >&5
-   echo "$as_me:7932: \$? = $ac_status" >&5
+   echo "$as_me:7961: \$? = $ac_status" >&5
    if (exit $ac_status) && test -s out/conftest2.$ac_objext
    then
      # The compiler can only warn and ignore the option if not recognized
@@ -10173,7 +10202,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 10176 "configure"
+#line 10205 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -10271,7 +10300,7 @@ else
   lt_dlunknown=0; lt_dlno_uscore=1; lt_dlneed_uscore=2
   lt_status=$lt_dlunknown
   cat > conftest.$ac_ext <<EOF
-#line 10274 "configure"
+#line 10303 "configure"
 #include "confdefs.h"
 
 #if HAVE_DLFCN_H
@@ -32170,6 +32199,190 @@ echo "$as_me: error: could not locate suitable ODBC library" >&2;}
 	fi
 fi
 
+ol_link_ndb=no
+if test $ol_enable_ndb != no ; then
+	# Extract the first word of "mysql_config", so it can be a program name with args.
+set dummy mysql_config; ac_word=$2
+echo "$as_me:$LINENO: checking for $ac_word" >&5
+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6
+if test "${ac_cv_prog_MYSQL+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  if test -n "$MYSQL"; then
+  ac_cv_prog_MYSQL="$MYSQL" # Let the user override the test.
+else
+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR
+for as_dir in $PATH
+do
+  IFS=$as_save_IFS
+  test -z "$as_dir" && as_dir=.
+  for ac_exec_ext in '' $ac_executable_extensions; do
+  if $as_executable_p "$as_dir/$ac_word$ac_exec_ext"; then
+    ac_cv_prog_MYSQL="yes"
+    echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5
+    break 2
+  fi
+done
+done
+
+fi
+fi
+MYSQL=$ac_cv_prog_MYSQL
+if test -n "$MYSQL"; then
+  echo "$as_me:$LINENO: result: $MYSQL" >&5
+echo "${ECHO_T}$MYSQL" >&6
+else
+  echo "$as_me:$LINENO: result: no" >&5
+echo "${ECHO_T}no" >&6
+fi
+
+	if test "$MYSQL" != yes ; then
+		{ { echo "$as_me:$LINENO: error: could not locate mysql_config" >&5
+echo "$as_me: error: could not locate mysql_config" >&2;}
+   { (exit 1); exit 1; }; }
+	fi
+
+	SQL_INC=`mysql_config --include`
+	SLAPD_NDB_INCS="$SQL_INC $SQL_INC/storage/ndb $SQL_INC/storage/ndb/ndbapi"
+
+	save_CPPFLAGS="$CPPFLAGS"
+	CPPFLAGS="$SLAPD_NDB_INCS"
+	echo "$as_me:$LINENO: checking for NdbApi.hpp" >&5
+echo $ECHO_N "checking for NdbApi.hpp... $ECHO_C" >&6
+	cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <NdbApi.hpp>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  echo "$as_me:$LINENO: result: yes" >&5
+echo "${ECHO_T}yes" >&6
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  { { echo "$as_me:$LINENO: error: could not locate NdbApi headers" >&5
+echo "$as_me: error: could not locate NdbApi headers" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+rm -f conftest.err conftest.$ac_ext
+	CPPFLAGS="$save_CPPFLAGS"
+
+	SQL_LIB=`mysql_config --libs_r`
+	SLAPD_NDB_LIBS="$SQL_LIB -lndbclient -lstdc++"
+
+	save_LDFLAGS="$LDFLAGS"
+	save_LIBS="$LIBS"
+	LDFLAGS="$SQL_LIB"
+
+echo "$as_me:$LINENO: checking for ndb_init in -lndbclient" >&5
+echo $ECHO_N "checking for ndb_init in -lndbclient... $ECHO_C" >&6
+if test "${ac_cv_lib_ndbclient_ndb_init+set}" = set; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  ac_check_lib_save_LIBS=$LIBS
+LIBS="-lndbclient -lstdc++ $LIBS"
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+
+/* Override any gcc2 internal prototype to avoid an error.  */
+#ifdef __cplusplus
+extern "C"
+#endif
+/* We use char because int might match the return type of a gcc2
+   builtin and then its argument prototype would still apply.  */
+char ndb_init ();
+int
+main ()
+{
+ndb_init ();
+  ;
+  return 0;
+}
+_ACEOF
+rm -f conftest.$ac_objext conftest$ac_exeext
+if { (eval echo "$as_me:$LINENO: \"$ac_link\"") >&5
+  (eval $ac_link) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest$ac_exeext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_cv_lib_ndbclient_ndb_init=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_cv_lib_ndbclient_ndb_init=no
+fi
+rm -f conftest.err conftest.$ac_objext \
+      conftest$ac_exeext conftest.$ac_ext
+LIBS=$ac_check_lib_save_LIBS
+fi
+echo "$as_me:$LINENO: result: $ac_cv_lib_ndbclient_ndb_init" >&5
+echo "${ECHO_T}$ac_cv_lib_ndbclient_ndb_init" >&6
+if test $ac_cv_lib_ndbclient_ndb_init = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define HAVE_LIBNDBCLIENT 1
+_ACEOF
+
+  LIBS="-lndbclient $LIBS"
+
+else
+
+		{ { echo "$as_me:$LINENO: error: could not locate ndbclient library" >&5
+echo "$as_me: error: could not locate ndbclient library" >&2;}
+   { (exit 1); exit 1; }; }
+
+fi
+
+	LIBS="$save_LIBS"
+	LDFLAGS="$save_LDFLAGS"
+
+	if test "$ol_enable_ndb" = yes ; then
+		SLAPD_LIBS="$SLAPD_LIBS \$(SLAPD_NDB_LIBS)"
+	fi
+fi
+
 ol_icu=no
 
 for ac_header in unicode/utypes.h
@@ -40488,6 +40701,23 @@ _ACEOF
 
 fi
 
+if test "$ol_enable_ndb" != no ; then
+	BUILD_SLAPD=yes
+	BUILD_NDB=$ol_enable_ndb
+	if test "$ol_enable_ndb" = mod ; then
+		SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-ndb"
+		MFLAG=SLAPD_MOD_DYNAMIC
+	else
+		SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-ndb"
+		MFLAG=SLAPD_MOD_STATIC
+	fi
+
+cat >>confdefs.h <<_ACEOF
+#define SLAPD_NDB $MFLAG
+_ACEOF
+
+fi
+
 if test "$ol_enable_null" != no ; then
 	BUILD_SLAPD=yes
 	BUILD_NULL=$ol_enable_null
@@ -40992,6 +41222,9 @@ fi
 
 
 
+
+
+
 
 
 
@@ -41003,7 +41236,7 @@ if test "${with_xxinstall+set}" = set; then
 fi;
 
 
-                                                                                                                                                                                                                                                                                                                                                                                                      ac_config_files="$ac_config_files Makefile:build/top.mk:Makefile.in:build/dir.mk doc/Makefile:build/top.mk:doc/Makefile.in:build/dir.mk doc/man/Makefile:build/top.mk:doc/man/Makefile.in:build/dir.mk doc/man/man1/Makefile:build/top.mk:doc/man/man1/Makefile.in:build/man.mk doc/man/man3/Makefile:build/top.mk:doc/man/man3/Makefile.in:build/man.mk doc/man/man5/Makefile:build/top.mk:doc/man/man5/Makefile.in:build/man.mk doc/man/man8/Makefile:build/top.mk:doc/man/man8/Makefile.in:build/man.mk clients/Makefile:build/top.mk:clients/Makefile.in:build/dir.mk clients/tools/Makefile:build/top.mk:clients/tools/Makefile.in:build/rules.mk include/Makefile:build/top.mk:include/Makefile.in libraries/Makefile:build/top.mk:libraries/Makefile.in:build/dir.mk libraries/liblber/Makefile:build/top.mk:libraries/liblber/Makefile.in:build/lib.mk:build/lib-shared.mk libraries/libldap/Makefile:build/top.mk:libraries/libldap/Makefile.in:build/lib.mk:build/lib-shared.mk libraries/libldap_r/Makefile:build/top.mk:libraries/libldap_r/Makefile.in:build/lib.mk:build/lib-shared.mk libraries/liblunicode/Makefile:build/top.mk:libraries/liblunicode/Makefile.in:build/lib.mk:build/lib-static.mk libraries/liblutil/Makefile:build/top.mk:libraries/liblutil/Makefile.in:build/lib.mk:build/lib-static.mk libraries/librewrite/Makefile:build/top.mk:libraries/librewrite/Makefile.in:build/lib.mk:build/lib-static.mk servers/Makefile:build/top.mk:servers/Makefile.in:build/dir.mk servers/slapd/Makefile:build/top.mk:servers/slapd/Makefile.in:build/srv.mk servers/slapd/back-bdb/Makefile:build/top.mk:servers/slapd/back-bdb/Makefile.in:build/mod.mk servers/slapd/back-dnssrv/Makefile:build/top.mk:servers/slapd/back-dnssrv/Makefile.in:build/mod.mk servers/slapd/back-hdb/Makefile:build/top.mk:servers/slapd/back-hdb/Makefile.in:build/mod.mk servers/slapd/back-ldap/Makefile:build/top.mk:servers/slapd/back-ldap/Makefile.in:build/mod.mk servers/slapd/back-ldif/Makefile:build/top.mk:servers/slapd/back-ldif/Makefile.in:build/mod.mk servers/slapd/back-meta/Makefile:build/top.mk:servers/slapd/back-meta/Makefile.in:build/mod.mk servers/slapd/back-monitor/Makefile:build/top.mk:servers/slapd/back-monitor/Makefile.in:build/mod.mk servers/slapd/back-null/Makefile:build/top.mk:servers/slapd/back-null/Makefile.in:build/mod.mk servers/slapd/back-passwd/Makefile:build/top.mk:servers/slapd/back-passwd/Makefile.in:build/mod.mk servers/slapd/back-perl/Makefile:build/top.mk:servers/slapd/back-perl/Makefile.in:build/mod.mk servers/slapd/back-relay/Makefile:build/top.mk:servers/slapd/back-relay/Makefile.in:build/mod.mk servers/slapd/back-shell/Makefile:build/top.mk:servers/slapd/back-shell/Makefile.in:build/mod.mk servers/slapd/back-sock/Makefile:build/top.mk:servers/slapd/back-sock/Makefile.in:build/mod.mk servers/slapd/back-sql/Makefile:build/top.mk:servers/slapd/back-sql/Makefile.in:build/mod.mk servers/slapd/shell-backends/Makefile:build/top.mk:servers/slapd/shell-backends/Makefile.in:build/srv.mk servers/slapd/slapi/Makefile:build/top.mk:servers/slapd/slapi/Makefile.in:build/lib.mk:build/lib-shared.mk servers/slapd/overlays/Makefile:build/top.mk:servers/slapd/overlays/Makefile.in:build/lib.mk tests/Makefile:build/top.mk:tests/Makefile.in:build/dir.mk tests/run tests/progs/Makefile:build/top.mk:tests/progs/Makefile.in:build/rules.mk"
+                                                                                                                                                                                                                                                                                                                                                                                                                ac_config_files="$ac_config_files Makefile:build/top.mk:Makefile.in:build/dir.mk doc/Makefile:build/top.mk:doc/Makefile.in:build/dir.mk doc/man/Makefile:build/top.mk:doc/man/Makefile.in:build/dir.mk doc/man/man1/Makefile:build/top.mk:doc/man/man1/Makefile.in:build/man.mk doc/man/man3/Makefile:build/top.mk:doc/man/man3/Makefile.in:build/man.mk doc/man/man5/Makefile:build/top.mk:doc/man/man5/Makefile.in:build/man.mk doc/man/man8/Makefile:build/top.mk:doc/man/man8/Makefile.in:build/man.mk clients/Makefile:build/top.mk:clients/Makefile.in:build/dir.mk clients/tools/Makefile:build/top.mk:clients/tools/Makefile.in:build/rules.mk include/Makefile:build/top.mk:include/Makefile.in libraries/Makefile:build/top.mk:libraries/Makefile.in:build/dir.mk libraries/liblber/Makefile:build/top.mk:libraries/liblber/Makefile.in:build/lib.mk:build/lib-shared.mk libraries/libldap/Makefile:build/top.mk:libraries/libldap/Makefile.in:build/lib.mk:build/lib-shared.mk libraries/libldap_r/Makefile:build/top.mk:libraries/libldap_r/Makefile.in:build/lib.mk:build/lib-shared.mk libraries/liblunicode/Makefile:build/top.mk:libraries/liblunicode/Makefile.in:build/lib.mk:build/lib-static.mk libraries/liblutil/Makefile:build/top.mk:libraries/liblutil/Makefile.in:build/lib.mk:build/lib-static.mk libraries/librewrite/Makefile:build/top.mk:libraries/librewrite/Makefile.in:build/lib.mk:build/lib-static.mk servers/Makefile:build/top.mk:servers/Makefile.in:build/dir.mk servers/slapd/Makefile:build/top.mk:servers/slapd/Makefile.in:build/srv.mk servers/slapd/back-bdb/Makefile:build/top.mk:servers/slapd/back-bdb/Makefile.in:build/mod.mk servers/slapd/back-dnssrv/Makefile:build/top.mk:servers/slapd/back-dnssrv/Makefile.in:build/mod.mk servers/slapd/back-hdb/Makefile:build/top.mk:servers/slapd/back-hdb/Makefile.in:build/mod.mk servers/slapd/back-ldap/Makefile:build/top.mk:servers/slapd/back-ldap/Makefile.in:build/mod.mk servers/slapd/back-ldif/Makefile:build/top.mk:servers/slapd/back-ldif/Makefile.in:build/mod.mk servers/slapd/back-meta/Makefile:build/top.mk:servers/slapd/back-meta/Makefile.in:build/mod.mk servers/slapd/back-monitor/Makefile:build/top.mk:servers/slapd/back-monitor/Makefile.in:build/mod.mk servers/slapd/back-ndb/Makefile:build/top.mk:servers/slapd/back-ndb/Makefile.in:build/mod.mk servers/slapd/back-null/Makefile:build/top.mk:servers/slapd/back-null/Makefile.in:build/mod.mk servers/slapd/back-passwd/Makefile:build/top.mk:servers/slapd/back-passwd/Makefile.in:build/mod.mk servers/slapd/back-perl/Makefile:build/top.mk:servers/slapd/back-perl/Makefile.in:build/mod.mk servers/slapd/back-relay/Makefile:build/top.mk:servers/slapd/back-relay/Makefile.in:build/mod.mk servers/slapd/back-shell/Makefile:build/top.mk:servers/slapd/back-shell/Makefile.in:build/mod.mk servers/slapd/back-sock/Makefile:build/top.mk:servers/slapd/back-sock/Makefile.in:build/mod.mk servers/slapd/back-sql/Makefile:build/top.mk:servers/slapd/back-sql/Makefile.in:build/mod.mk servers/slapd/shell-backends/Makefile:build/top.mk:servers/slapd/shell-backends/Makefile.in:build/srv.mk servers/slapd/slapi/Makefile:build/top.mk:servers/slapd/slapi/Makefile.in:build/lib.mk:build/lib-shared.mk servers/slapd/overlays/Makefile:build/top.mk:servers/slapd/overlays/Makefile.in:build/lib.mk tests/Makefile:build/top.mk:tests/Makefile.in:build/dir.mk tests/run tests/progs/Makefile:build/top.mk:tests/progs/Makefile.in:build/rules.mk"
 
 
           ac_config_commands="$ac_config_commands default"
@@ -41541,6 +41774,7 @@ do
   "servers/slapd/back-ldif/Makefile" ) CONFIG_FILES="$CONFIG_FILES servers/slapd/back-ldif/Makefile:build/top.mk:servers/slapd/back-ldif/Makefile.in:build/mod.mk" ;;
   "servers/slapd/back-meta/Makefile" ) CONFIG_FILES="$CONFIG_FILES servers/slapd/back-meta/Makefile:build/top.mk:servers/slapd/back-meta/Makefile.in:build/mod.mk" ;;
   "servers/slapd/back-monitor/Makefile" ) CONFIG_FILES="$CONFIG_FILES servers/slapd/back-monitor/Makefile:build/top.mk:servers/slapd/back-monitor/Makefile.in:build/mod.mk" ;;
+  "servers/slapd/back-ndb/Makefile" ) CONFIG_FILES="$CONFIG_FILES servers/slapd/back-ndb/Makefile:build/top.mk:servers/slapd/back-ndb/Makefile.in:build/mod.mk" ;;
   "servers/slapd/back-null/Makefile" ) CONFIG_FILES="$CONFIG_FILES servers/slapd/back-null/Makefile:build/top.mk:servers/slapd/back-null/Makefile.in:build/mod.mk" ;;
   "servers/slapd/back-passwd/Makefile" ) CONFIG_FILES="$CONFIG_FILES servers/slapd/back-passwd/Makefile:build/top.mk:servers/slapd/back-passwd/Makefile.in:build/mod.mk" ;;
   "servers/slapd/back-perl/Makefile" ) CONFIG_FILES="$CONFIG_FILES servers/slapd/back-perl/Makefile:build/top.mk:servers/slapd/back-perl/Makefile.in:build/mod.mk" ;;
@@ -41718,6 +41952,7 @@ s,@PERLBIN@,$PERLBIN,;t t
 s,@OL_MKDEP@,$OL_MKDEP,;t t
 s,@OL_MKDEP_FLAGS@,$OL_MKDEP_FLAGS,;t t
 s,@LTSTATIC@,$LTSTATIC,;t t
+s,@MYSQL@,$MYSQL,;t t
 s,@LIBOBJS@,$LIBOBJS,;t t
 s,@LIBSRCS@,$LIBSRCS,;t t
 s,@PLAT@,$PLAT,;t t
@@ -41736,6 +41971,7 @@ s,@BUILD_HDB@,$BUILD_HDB,;t t
 s,@BUILD_LDAP@,$BUILD_LDAP,;t t
 s,@BUILD_META@,$BUILD_META,;t t
 s,@BUILD_MONITOR@,$BUILD_MONITOR,;t t
+s,@BUILD_NDB@,$BUILD_NDB,;t t
 s,@BUILD_NULL@,$BUILD_NULL,;t t
 s,@BUILD_PASSWD@,$BUILD_PASSWD,;t t
 s,@BUILD_RELAY@,$BUILD_RELAY,;t t
@@ -41765,6 +42001,8 @@ s,@BUILD_VALSORT@,$BUILD_VALSORT,;t t
 s,@LDAP_LIBS@,$LDAP_LIBS,;t t
 s,@SLAPD_LIBS@,$SLAPD_LIBS,;t t
 s,@BDB_LIBS@,$BDB_LIBS,;t t
+s,@SLAPD_NDB_LIBS@,$SLAPD_NDB_LIBS,;t t
+s,@SLAPD_NDB_INCS@,$SLAPD_NDB_INCS,;t t
 s,@LTHREAD_LIBS@,$LTHREAD_LIBS,;t t
 s,@LUTIL_LIBS@,$LUTIL_LIBS,;t t
 s,@WRAP_LIBS@,$WRAP_LIBS,;t t
diff --git a/configure.in b/configure.in
index e2a5166124cea14eb549d973731945521a6d3fe2..90bd03bec53e42d13a5e480cb102610ac5eefce7 100644
--- a/configure.in
+++ b/configure.in
@@ -286,6 +286,7 @@ Backends="bdb \
 	ldap \
 	meta \
 	monitor \
+	ndb \
 	null \
 	passwd \
 	perl \
@@ -311,6 +312,8 @@ OL_ARG_ENABLE(meta,[    --enable-meta	  enable metadirectory backend],
 	no, [no yes mod], ol_enable_backends)dnl
 OL_ARG_ENABLE(monitor,[    --enable-monitor	  enable monitor backend],
 	yes, [no yes mod], ol_enable_backends)dnl
+OL_ARG_ENABLE(ndb,[    --enable-ndb	  enable MySQL NDB Cluster backend],
+	no, [no yes mod], ol_enable_backends)dnl
 OL_ARG_ENABLE(null,[    --enable-null	  enable null backend],
 	no, [no yes mod], ol_enable_backends)dnl
 OL_ARG_ENABLE(passwd,[    --enable-passwd	  enable passwd backend],
@@ -460,6 +463,7 @@ elif test $ol_enable_modules != yes &&
 	test $ol_enable_ldap = no &&
 	test $ol_enable_meta = no &&
 	test $ol_enable_monitor = no &&
+	test $ol_enable_ndb = no &&
 	test $ol_enable_null = no &&
 	test $ol_enable_passwd = no &&
 	test $ol_enable_perl = no &&
@@ -500,6 +504,8 @@ dnl ----------------------------------------------------------------
 dnl Initialize vars
 LDAP_LIBS=
 BDB_LIBS=
+SLAPD_NDB_LIBS=
+SLAPD_NDB_INCS=
 LTHREAD_LIBS=
 LUTIL_LIBS=
 
@@ -518,6 +524,7 @@ BUILD_HDB=no
 BUILD_LDAP=no
 BUILD_META=no
 BUILD_MONITOR=no
+BUILD_NDB=no
 BUILD_NULL=no
 BUILD_PASSWD=no
 BUILD_PERL=no
@@ -1973,6 +1980,47 @@ if test $ol_enable_sql != no ; then
 	fi
 fi
 
+dnl ----------------------------------------------------------------
+dnl MySQL NDBapi
+dnl Note: uses C++, but we don't want to add C++ test overhead to
+dnl the rest of the libtool machinery.
+ol_link_ndb=no
+if test $ol_enable_ndb != no ; then
+	AC_CHECK_PROG(MYSQL,mysql_config,yes)
+	if test "$MYSQL" != yes ; then
+		AC_MSG_ERROR([could not locate mysql_config])
+	fi
+
+	SQL_INC=`mysql_config --include`
+	SLAPD_NDB_INCS="$SQL_INC $SQL_INC/storage/ndb $SQL_INC/storage/ndb/ndbapi"
+
+	save_CPPFLAGS="$CPPFLAGS"
+	CPPFLAGS="$SLAPD_NDB_INCS"
+	AC_MSG_CHECKING(for NdbApi.hpp)
+	AC_PREPROC_IFELSE(
+		[AC_LANG_SOURCE([[#include <NdbApi.hpp>]])],
+			AC_MSG_RESULT(yes),
+			AC_MSG_ERROR([could not locate NdbApi headers])
+	)
+	CPPFLAGS="$save_CPPFLAGS"
+
+	SQL_LIB=`mysql_config --libs_r`
+	SLAPD_NDB_LIBS="$SQL_LIB -lndbclient -lstdc++"
+
+	save_LDFLAGS="$LDFLAGS"
+	save_LIBS="$LIBS"
+	LDFLAGS="$SQL_LIB"
+	AC_CHECK_LIB(ndbclient,ndb_init,[],[
+		AC_MSG_ERROR([could not locate ndbclient library])
+	],[-lstdc++])
+	LIBS="$save_LIBS"
+	LDFLAGS="$save_LDFLAGS"
+
+	if test "$ol_enable_ndb" = yes ; then
+		SLAPD_LIBS="$SLAPD_LIBS \$(SLAPD_NDB_LIBS)"
+	fi
+fi
+
 dnl ----------------------------------------------------------------
 dnl International Components for Unicode
 OL_ICU
@@ -2579,6 +2627,19 @@ if test "$ol_enable_meta" != no ; then
 	AC_DEFINE_UNQUOTED(SLAPD_META,$MFLAG,[define to support LDAP Metadirectory backend])
 fi
 
+if test "$ol_enable_ndb" != no ; then
+	BUILD_SLAPD=yes
+	BUILD_NDB=$ol_enable_ndb
+	if test "$ol_enable_ndb" = mod ; then
+		SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-ndb"
+		MFLAG=SLAPD_MOD_DYNAMIC
+	else
+		SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-ndb"
+		MFLAG=SLAPD_MOD_STATIC
+	fi
+	AC_DEFINE_UNQUOTED(SLAPD_NDB,$MFLAG,[define to support NDB backend])
+fi
+
 if test "$ol_enable_null" != no ; then
 	BUILD_SLAPD=yes
 	BUILD_NULL=$ol_enable_null
@@ -2923,6 +2984,7 @@ dnl backends
   AC_SUBST(BUILD_LDAP)
   AC_SUBST(BUILD_META)
   AC_SUBST(BUILD_MONITOR)
+  AC_SUBST(BUILD_NDB)
   AC_SUBST(BUILD_NULL)
   AC_SUBST(BUILD_PASSWD)
   AC_SUBST(BUILD_RELAY)
@@ -2954,6 +3016,8 @@ dnl overlays
 AC_SUBST(LDAP_LIBS)
 AC_SUBST(SLAPD_LIBS)
 AC_SUBST(BDB_LIBS)
+AC_SUBST(SLAPD_NDB_LIBS)
+AC_SUBST(SLAPD_NDB_INCS)
 AC_SUBST(LTHREAD_LIBS)
 AC_SUBST(LUTIL_LIBS)
 AC_SUBST(WRAP_LIBS)
@@ -3024,6 +3088,7 @@ AC_CONFIG_FILES([Makefile:build/top.mk:Makefile.in:build/dir.mk]
 [servers/slapd/back-ldif/Makefile:build/top.mk:servers/slapd/back-ldif/Makefile.in:build/mod.mk]
 [servers/slapd/back-meta/Makefile:build/top.mk:servers/slapd/back-meta/Makefile.in:build/mod.mk]
 [servers/slapd/back-monitor/Makefile:build/top.mk:servers/slapd/back-monitor/Makefile.in:build/mod.mk]
+[servers/slapd/back-ndb/Makefile:build/top.mk:servers/slapd/back-ndb/Makefile.in:build/mod.mk]
 [servers/slapd/back-null/Makefile:build/top.mk:servers/slapd/back-null/Makefile.in:build/mod.mk]
 [servers/slapd/back-passwd/Makefile:build/top.mk:servers/slapd/back-passwd/Makefile.in:build/mod.mk]
 [servers/slapd/back-perl/Makefile:build/top.mk:servers/slapd/back-perl/Makefile.in:build/mod.mk]
diff --git a/doc/man/man5/slapd-ndb.5 b/doc/man/man5/slapd-ndb.5
new file mode 100644
index 0000000000000000000000000000000000000000..c8013760d73c138e71dadd373d81b76986bdc60b
--- /dev/null
+++ b/doc/man/man5/slapd-ndb.5
@@ -0,0 +1,125 @@
+.TH SLAPD-NDB 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2008 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply.  See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd-ndb \- MySQL NDB backend to slapd
+.SH SYNOPSIS
+.B ETCDIR/slapd.conf
+.SH DESCRIPTION
+The \fBndb\fP backend to
+.BR slapd (8)
+uses the MySQL Cluster package to store data, through its NDB API.
+It provides fault tolerance with extreme scalability, along with
+a degree of SQL compatibility.
+.LP
+This backend is designed to store LDAP information using tables that
+are also visible from SQL. It uses a higher level SQL API for creating
+these tables, while using the low level NDB API for storing and
+retrieving the data within these tables. The NDB Cluster engine
+allows data to be partitioned across multiple data nodes, and this
+backend allows multiple slapd instances to operate against a given
+database concurrently.
+.LP
+The general approach is to use distinct tables for each LDAP object class.
+Entries comprised of multiple object classes will have their data
+spread across multiple tables. The data tables use a 64 bit entryID
+as their primary key. The DIT hierarchy is maintained in a separate
+table, which maps DNs to entryIDs.
+.LP
+This backend is experimental. While intended to be a general-purpose
+backend, it is currently missing a number of common LDAP features.
+See the \fBTODO\fP file in the source directory for details.
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the \fBndb\fP backend database.
+That is, they must follow a "database ndb" line and
+come before any subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+
+.SH DATA SOURCE CONFIGURATION
+
+.TP
+.B dbhost <hostname>
+The name or IP address of the host running the MySQL server. The default
+is "localhost". On Unix systems, the connection to a local server is made
+using a Unix Domain socket, whose path is specified using the
+.B dbsocket
+directive.
+.TP
+.B dbuser <username>
+The MySQL login ID to use when connecting to the MySQL server. The chosen
+user must have sufficient privileges to manipulate the SQL tables in the
+target database.
+.TP
+.B dbpasswd <password>
+The password for the \fBdbuser\fP.
+.TP
+.B dbname <database name>
+The name of the MySQL database to use.
+.TP
+.B dbport <port>
+The port number to use for the TCP connection to the MySQL server.
+.TP
+.B dbsocket <path>
+The socket to be used for connecting to a local MySQL server.
+.TP
+.B dbflag <integer>
+Client flags for the MySQL session. See the MySQL documentation for details.
+.TP
+.B dbconnect <connectstring>
+The name or IP address of the host running the cluster manager. The default
+is "localhost".
+.TP
+.B dbconnections <integer>
+The number of cluster connections to establish. Using up to 4 may improve
+performance under heavier load. The default is 1.
+
+.SH SCHEMA CONFIGURATION
+.TP
+.B attrlen <attribute> <length>
+Specify the column length to use for a particular attribute. LDAP attributes are
+stored in individual columns of the SQL tables. The maximum column lengths for
+each column must be specified when creating these tables. If a length constraint
+was specified in the attribute's LDAP schema definition, that value will be used
+by default. If the schema didn't specify a constraint, the default is 128 bytes.
+Currently the maximum is 1024.
+.TP
+.B index <attr[,attr...]>
+Specify a list of attributes for which indexing should be maintained.
+Currently there is no support for substring indexing; a single index structure
+provides presence, equality, and inequality indexing for the specified attributes.
+.TP
+.B attrset <set> <attrs>
+Specify a list of attributes to be treated as an attribute set. This directive
+creates a table named \fIset\fP which will contain all of the listed attributes.
+Ordinarily an attribute resides in a table named by an object class that uses
+the attribute. However, attributes are only allowed to appear in a single table.
+For attributes that are derived from an inherited object class definition,
+the attribute will only be stored in the superior class's table.
+Attribute sets should be defined for any attributes that are used in multiple
+unrelated object classes, i.e., classes that are not connected by a simple
+inheritance chain.
+.SH ACCESS CONTROL
+The 
+.B ndb
+backend honors most access control semantics as indicated in
+.BR slapd.access (5).
+.SH FILES
+.TP
+.B ETCDIR/slapd.conf
+default 
+.B slapd 
+configuration file
+.SH SEE ALSO
+.BR slapd.conf (5),
+.BR slapd (8),
+.BR slapadd (8),
+.BR slapcat (8),
+.BR slapindex (8),
+MySQL Cluster documentation.
+.SH AUTHOR
+Howard Chu, with assistance from Johan Andersson et al @ MySQL.
diff --git a/doc/man/man5/slapd.backends.5 b/doc/man/man5/slapd.backends.5
index 078d9ab13491f0319e68315ddc2c80fec189fe12..d6e95546494ed889bd7122241c509d7c13cf3ca4 100644
--- a/doc/man/man5/slapd.backends.5
+++ b/doc/man/man5/slapd.backends.5
@@ -71,6 +71,11 @@ daemon. Only a single instance of the
 .B monitor
 backend may be defined.
 .TP
+.B ndb
+This backend is experimental.
+It uses the transactional database interface of the MySQL Cluster Engine
+(NDB) to store data.
+.TP
 .B null
 Operations in this backend succeed but do nothing.
 .TP
diff --git a/include/portable.hin b/include/portable.hin
index f3093533ac036212a43c6ffe4e1819d094b1be11..787d083ca8684935afe8d21f244147aef121edaa 100644
--- a/include/portable.hin
+++ b/include/portable.hin
@@ -945,6 +945,9 @@
 /* define to support cn=Monitor backend */
 #undef SLAPD_MONITOR
 
+/* define to support NDB backend */
+#undef SLAPD_NDB
+
 /* define to support NULL backend */
 #undef SLAPD_NULL
 
diff --git a/servers/slapd/back-ndb/Makefile.in b/servers/slapd/back-ndb/Makefile.in
new file mode 100644
index 0000000000000000000000000000000000000000..84253fd4e1f66f24e1c8340d865cf57fe9c3282e
--- /dev/null
+++ b/servers/slapd/back-ndb/Makefile.in
@@ -0,0 +1,59 @@
+# Makefile.in for back-ndb
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2008 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This work was initially developed by Howard Chu for inclusion
+## in OpenLDAP Software. This work was sponsored by MySQL.
+
+SRCS = init.cpp tools.cpp config.cpp ndbio.cpp \
+	add.cpp bind.cpp compare.cpp delete.cpp modify.cpp modrdn.cpp search.cpp
+
+OBJS = init.lo tools.lo config.lo ndbio.lo \
+	add.lo bind.lo compare.lo delete.lo modify.lo modrdn.lo search.lo
+
+LDAP_INCDIR= ../../../include       
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-ndb"
+BUILD_MOD = @BUILD_NDB@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_NDB@_DEFS)
+MOD_LIBS = $(SLAPD_NDB_LIBS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_R_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_ndb
+
+XINCPATH = -I.. -I$(srcdir)/.. @SLAPD_NDB_INCS@
+XDEFS = $(MODULES_CPPFLAGS)
+
+AC_CXX = g++
+CXX = $(AC_CXX)
+LTCXX_MOD = $(LIBTOOL) $(LTONLY_MOD) --mode=compile \
+	$(CXX) $(LT_CFLAGS) $(LT_CPPFLAGS) $(MOD_DEFS) -c
+
+all-local-lib:	../.backend
+
+.SUFFIXES: .c .o .lo .cpp
+
+.cpp.lo:
+	$(LTCXX_MOD) $<
+
+../.backend: lib$(LIBBASE).a
+	@touch $@
+
diff --git a/servers/slapd/back-ndb/TODO b/servers/slapd/back-ndb/TODO
new file mode 100644
index 0000000000000000000000000000000000000000..03939546018f47b255a01d08502f81159e08078a
--- /dev/null
+++ b/servers/slapd/back-ndb/TODO
@@ -0,0 +1,6 @@
+LDAP features not currently supported:
+
+tagged attributes
+aliases
+substring indexing
+subtree rename
diff --git a/servers/slapd/back-ndb/add.cpp b/servers/slapd/back-ndb/add.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8a5ca68fec294a4376cc0a3cde6767e22c8c6fa2
--- /dev/null
+++ b/servers/slapd/back-ndb/add.cpp
@@ -0,0 +1,333 @@
+/* add.cpp - ldap NDB back-end add routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-ndb.h"
+
+extern "C" int
+ndb_back_add(Operation *op, SlapReply *rs )
+{
+	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+	Entry		p = {0};
+	Attribute	poc;
+	char textbuf[SLAP_TEXT_BUFLEN];
+	size_t textlen = sizeof textbuf;
+	AttributeDescription *children = slap_schema.si_ad_children;
+	AttributeDescription *entry = slap_schema.si_ad_entry;
+	NdbArgs NA;
+	NdbRdns rdns;
+	struct berval matched;
+	struct berval pdn, pndn;
+
+	int		num_retries = 0;
+	int		success;
+
+	LDAPControl **postread_ctrl = NULL;
+	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+	int num_ctrls = 0;
+
+	Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(ndb_back_add) ": %s\n",
+		op->oq_add.rs_e->e_name.bv_val, 0, 0);
+
+	ctrls[num_ctrls] = 0;
+
+	/* check entry's schema */
+	rs->sr_err = entry_schema_check( op, op->oq_add.rs_e, NULL,
+		get_relax(op), 1, &rs->sr_text, textbuf, textlen );
+	if ( rs->sr_err != LDAP_SUCCESS ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_add) ": entry failed schema check: "
+			"%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
+		goto return_results;
+	}
+
+	/* add opattrs to shadow as well, only missing attrs will actually
+	 * be added; helps compatibility with older OL versions */
+	rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
+	if ( rs->sr_err != LDAP_SUCCESS ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_add) ": entry failed op attrs add: "
+			"%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
+		goto return_results;
+	}
+
+	/* Get our NDB handle */
+	rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+
+	/*
+	 * Get the parent dn and see if the corresponding entry exists.
+	 */
+	if ( be_issuffix( op->o_bd, &op->oq_add.rs_e->e_nname ) ) {
+		pdn = slap_empty_bv;
+		pndn = slap_empty_bv;
+	} else {
+		dnParent( &op->ora_e->e_name, &pdn );
+		dnParent( &op->ora_e->e_nname, &pndn );
+	}
+	p.e_name = op->ora_e->e_name;
+	p.e_nname = op->ora_e->e_nname;
+
+	op->ora_e->e_id = NOID;
+	rdns.nr_num = 0;
+	NA.rdns = &rdns;
+
+	if( 0 ) {
+retry:	/* transaction retry */
+		NA.txn->close();
+		NA.txn = NULL;
+		if ( op->o_abandon ) {
+			rs->sr_err = SLAPD_ABANDON;
+			goto return_results;
+		}
+		ndb_trans_backoff( ++num_retries );
+	}
+
+	NA.txn = NA.ndb->startTransaction();
+	rs->sr_text = NULL;
+	if( !NA.txn ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_add) ": startTransaction failed: %s (%d)\n",
+			NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+
+	/* get entry or parent */
+	NA.e = &p;
+	NA.ocs = NULL;
+	rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
+	switch( rs->sr_err ) {
+	case 0:
+		rs->sr_err = LDAP_ALREADY_EXISTS;
+		goto return_results;
+	case LDAP_NO_SUCH_OBJECT:
+		break;
+#if 0
+	case DB_LOCK_DEADLOCK:
+	case DB_LOCK_NOTGRANTED:
+		goto retry;
+#endif
+	case LDAP_BUSY:
+		rs->sr_text = "ldap server busy";
+		goto return_results;
+	default:
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+
+	if ( NA.ocs ) {
+		int i;
+		for ( i=0; !BER_BVISNULL( &NA.ocs[i] ); i++ );
+		poc.a_numvals = i;
+		poc.a_desc = slap_schema.si_ad_objectClass;
+		poc.a_vals = NA.ocs;
+		poc.a_nvals = poc.a_vals;
+		poc.a_next = NULL;
+		p.e_attrs = &poc;
+	}
+
+	if ( ber_bvstrcasecmp( &pndn, &matched ) ) {
+		rs->sr_matched = matched.bv_val;
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_add) ": parent "
+			"does not exist\n", 0, 0, 0 );
+
+		rs->sr_text = "parent does not exist";
+		rs->sr_err = LDAP_NO_SUCH_OBJECT;
+		if ( p.e_attrs && is_entry_referral( &p )) {
+is_ref:			p.e_attrs = NULL;
+			ndb_entry_get_data( op, &NA, 0 );
+			rs->sr_ref = get_entry_referrals( op, &p );
+			rs->sr_err = LDAP_REFERRAL;
+			rs->sr_flags = REP_REF_MUSTBEFREED;
+			attrs_free( p.e_attrs );
+			p.e_attrs = NULL;
+		}
+		goto return_results;
+	}
+
+	p.e_name = pdn;
+	p.e_nname = pndn;
+	rs->sr_err = access_allowed( op, &p,
+		children, NULL, ACL_WADD, NULL );
+
+	if ( ! rs->sr_err ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_add) ": no write access to parent\n",
+			0, 0, 0 );
+		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+		rs->sr_text = "no write access to parent";
+		goto return_results;
+	}
+
+	if ( NA.ocs ) {
+		if ( is_entry_subentry( &p )) {
+			/* parent is a subentry, don't allow add */
+			Debug( LDAP_DEBUG_TRACE,
+				LDAP_XSTRING(ndb_back_add) ": parent is subentry\n",
+				0, 0, 0 );
+			rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+			rs->sr_text = "parent is a subentry";
+			goto return_results;
+		}
+
+		if ( is_entry_alias( &p ) ) {
+			/* parent is an alias, don't allow add */
+			Debug( LDAP_DEBUG_TRACE,
+				LDAP_XSTRING(ndb_back_add) ": parent is alias\n",
+				0, 0, 0 );
+			rs->sr_err = LDAP_ALIAS_PROBLEM;
+			rs->sr_text = "parent is an alias";
+			goto return_results;
+		}
+
+		if ( is_entry_referral( &p ) ) {
+			/* parent is a referral, don't allow add */
+			rs->sr_matched = p.e_name.bv_val;
+			goto is_ref;
+		}
+	}
+
+	rs->sr_err = access_allowed( op, op->ora_e,
+		entry, NULL, ACL_WADD, NULL );
+
+	if ( ! rs->sr_err ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_add) ": no write access to entry\n",
+			0, 0, 0 );
+		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+		rs->sr_text = "no write access to entry";
+		goto return_results;;
+	}
+
+	/* acquire entry ID */
+	if ( op->ora_e->e_id == NOID ) {
+		rs->sr_err = ndb_next_id( op->o_bd, NA.ndb, &op->ora_e->e_id );
+		if( rs->sr_err != 0 ) {
+			Debug( LDAP_DEBUG_TRACE,
+				LDAP_XSTRING(ndb_back_add) ": next_id failed (%d)\n",
+				rs->sr_err, 0, 0 );
+			rs->sr_err = LDAP_OTHER;
+			rs->sr_text = "internal error";
+			goto return_results;
+		}
+	}
+
+	if ( matched.bv_val )
+		rdns.nr_num++;
+	NA.e = op->ora_e;
+	/* dn2id index */
+	rs->sr_err = ndb_entry_put_info( op->o_bd, &NA, 0 );
+	if ( rs->sr_err ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_info failed (%d)\n",
+			rs->sr_err, 0, 0 );
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+
+	/* id2entry index */
+	rs->sr_err = ndb_entry_put_data( op->o_bd, &NA );
+	if ( rs->sr_err ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_data failed (%d) %s(%d)\n",
+			rs->sr_err, NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+
+	/* post-read */
+	if( op->o_postread ) {
+		if( postread_ctrl == NULL ) {
+			postread_ctrl = &ctrls[num_ctrls++];
+			ctrls[num_ctrls] = NULL;
+		}
+		if ( slap_read_controls( op, rs, op->oq_add.rs_e,
+			&slap_post_read_bv, postread_ctrl ) )
+		{
+			Debug( LDAP_DEBUG_TRACE,
+				"<=- " LDAP_XSTRING(ndb_back_add) ": post-read "
+				"failed!\n", 0, 0, 0 );
+			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+				/* FIXME: is it correct to abort
+				 * operation if control fails? */
+				goto return_results;
+			}
+		}
+	}
+
+	if ( op->o_noop ) {
+		if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
+			NdbOperation::AbortOnError, 1 )) != 0 ) {
+			rs->sr_text = "txn (no-op) failed";
+		} else {
+			rs->sr_err = LDAP_X_NO_OPERATION;
+		}
+
+	} else {
+		if(( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
+			NdbOperation::AbortOnError, 1 )) != 0 ) {
+			rs->sr_text = "txn_commit failed";
+		} else {
+			rs->sr_err = LDAP_SUCCESS;
+		}
+	}
+
+	if ( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_add) ": %s : %s (%d)\n",
+			rs->sr_text, NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+		rs->sr_err = LDAP_OTHER;
+		goto return_results;
+	}
+	NA.txn->close();
+	NA.txn = NULL;
+
+	Debug(LDAP_DEBUG_TRACE,
+		LDAP_XSTRING(ndb_back_add) ": added%s id=%08lx dn=\"%s\"\n",
+		op->o_noop ? " (no-op)" : "",
+		op->oq_add.rs_e->e_id, op->oq_add.rs_e->e_dn );
+
+	rs->sr_text = NULL;
+	if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+	success = rs->sr_err;
+	send_ldap_result( op, rs );
+	slap_graduate_commit_csn( op );
+
+	if( NA.txn != NULL ) {
+		NA.txn->execute( Rollback );
+		NA.txn->close();
+	}
+
+	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+		slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+		slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+	}
+
+	return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/attrsets.conf b/servers/slapd/back-ndb/attrsets.conf
new file mode 100644
index 0000000000000000000000000000000000000000..f135e0ab1ecff6729ca8377d8f8a547cfee1115c
--- /dev/null
+++ b/servers/slapd/back-ndb/attrsets.conf
@@ -0,0 +1,36 @@
+# Definition of useful attribute sets
+# from X.521 section 5
+#
+# TelecommunicationAttributeSet	ATTRIBUTE ::= {
+#	facsimileTelephoneNumber |
+#	internationalISDNNumber |
+#	telephoneNumber |
+#	teletexTerminalIdentifier |
+#	telexNumber |
+#	preferredDeliveryMethod |
+#	destinationIndicator |
+#	registeredAddress |
+#	x121Address }
+#
+# PostalAttributeSet	ATTRIBUTE ::= {
+#	physicalDeliveryOfficeName |
+#	postalAddress |
+#	postalCode |
+#	postOfficeBox |
+#	streetAddress }
+#
+# LocaleAttributeSet	ATTRIBUTE ::= {
+#	localityName |
+#	stateOrProvinceName |
+#	streetAddress }
+#
+# OrganizationalAttributeSet	ATTRIBUTE ::= {
+#	description |
+#	LocaleAttributeSet |
+#	PostalAttributeSet |
+#	TelecommunicationAttributeSet |
+#	businessCategory |
+#	seeAlso |
+#	searchGuide |
+#	userPassword }
+
diff --git a/servers/slapd/back-ndb/back-ndb.h b/servers/slapd/back-ndb/back-ndb.h
new file mode 100644
index 0000000000000000000000000000000000000000..eb240f389d95a925805a48b59d8cea21d6089a7f
--- /dev/null
+++ b/servers/slapd/back-ndb/back-ndb.h
@@ -0,0 +1,168 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#ifndef SLAPD_NDB_H
+#define SLAPD_NDB_H
+
+#include "slap.h"
+
+#include <mysql.h>
+#include <NdbApi.hpp>
+
+LDAP_BEGIN_DECL
+
+/* The general design is to use one relational table per objectclass. This is
+ * complicated by objectclass inheritance and auxiliary classes though.
+ *
+ * Attributes must only occur in a single table. For objectclasses that inherit
+ * from other classes, attributes defined in the superior class are only stored
+ * in the superior class' table. When multiple unrelated classes define the same
+ * attributes, an attributeSet should be defined instead, containing all of the
+ * common attributes.
+ *
+ * The no_set table lists which other attributeSets apply to the current
+ * objectClass. The no_attrs table lists all of the non-inherited attributes of
+ * the class, including those residing in an attributeSet.
+ *
+ * Usually the table is named identically to the objectClass, but it can also
+ * be explicitly named something else if needed.
+ */
+#define NDB_MAX_OCSETS	8
+
+struct ndb_attrinfo;
+
+typedef struct ndb_ocinfo {
+	struct berval no_name;	/* objectclass cname */
+	struct berval no_table;
+	ObjectClass *no_oc;
+	struct ndb_ocinfo *no_sets[NDB_MAX_OCSETS];
+	struct ndb_attrinfo **no_attrs;
+	int no_flag;
+	int no_nsets;
+	int no_nattrs;
+} NdbOcInfo;
+
+#define	NDB_INFO_ATLEN	0x01
+#define	NDB_INFO_ATSET	0x02
+#define	NDB_INFO_INDEX	0x04
+#define	NDB_INFO_ATBLOB	0x08
+
+typedef struct ndb_attrinfo {
+	struct berval na_name;	/* attribute cname */
+	AttributeDescription *na_desc;
+	AttributeType *na_attr;
+	NdbOcInfo *na_oi;
+	int na_flag;
+	int na_len;
+	int na_column;
+	int na_ixcol;
+} NdbAttrInfo;
+
+typedef struct ListNode {
+	struct ListNode *ln_next;
+	void *ln_data;
+} ListNode;
+
+#define	NDB_IS_OPEN(ni)	(ni->ni_cluster != NULL)
+
+struct ndb_info {
+	/* NDB connection */
+	char *ni_connectstr;
+	char *ni_dbname;
+	Ndb_cluster_connection **ni_cluster;
+
+	/* MySQL connection parameters */
+	MYSQL ni_sql;
+	char *ni_hostname;
+	char *ni_username;
+	char *ni_password;
+	char *ni_socket;
+	unsigned long ni_clflag;
+	unsigned int ni_port;
+
+	/* Search filter processing */
+	int ni_search_stack_depth;
+	void *ni_search_stack;
+
+#define	DEFAULT_SEARCH_STACK_DEPTH	16
+#define	MINIMUM_SEARCH_STACK_DEPTH	8
+
+	/* Schema config */
+	NdbOcInfo *ni_opattrs;
+	ListNode *ni_attridxs;
+	ListNode *ni_attrlens;
+	ListNode *ni_attrsets;
+	ListNode *ni_attrblobs;
+	ldap_pvt_thread_rdwr_t ni_ai_rwlock;
+	Avlnode *ni_ai_tree;
+	ldap_pvt_thread_rdwr_t ni_oc_rwlock;
+	Avlnode *ni_oc_tree;
+	int ni_nconns;	/* number of connections to open */
+	int ni_nextconn;	/* next conn to use */
+	ldap_pvt_thread_mutex_t ni_conn_mutex;
+};
+
+#define	NDB_MAX_RDNS	16
+#define	NDB_RDN_LEN	128
+#define	NDB_MAX_OCS	64
+
+#define	DN2ID_TABLE	"OL_dn2id"
+#define	EID_COLUMN	0U
+#define	VID_COLUMN	1U
+#define	OCS_COLUMN	1U
+#define	RDN_COLUMN	2U
+#define	IDX_COLUMN	(2U+NDB_MAX_RDNS)
+
+#define	NEXTID_TABLE	"OL_nextid"
+
+#define	NDB_OC_BUFLEN	1026	/* 1024 data plus 2 len bytes */
+
+#define	INDEX_NAME	"OL_index"
+
+typedef struct NdbRdns {
+	short nr_num;
+	char nr_buf[NDB_MAX_RDNS][NDB_RDN_LEN+1];
+} NdbRdns;
+
+typedef struct NdbOcs {
+	int no_ninfo;
+	int no_ntext;
+	int no_nitext;	/* number of implicit classes */
+	NdbOcInfo *no_info[NDB_MAX_OCS];
+	struct berval no_text[NDB_MAX_OCS];
+	struct berval no_itext[NDB_MAX_OCS];	/* implicit classes */
+} NdbOcs;
+
+typedef struct NdbArgs {
+	Ndb *ndb;
+	NdbTransaction *txn;
+	Entry *e;
+	NdbRdns *rdns;
+	struct berval *ocs;
+	int erdns;
+} NdbArgs;
+
+#define	NDB_NO_SUCH_OBJECT	626
+#define	NDB_ALREADY_EXISTS	630
+
+LDAP_END_DECL
+
+#include "proto-ndb.h"
+
+#endif
diff --git a/servers/slapd/back-ndb/bind.cpp b/servers/slapd/back-ndb/bind.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..005e5580c9422d871038258b13e0b412d31ce798
--- /dev/null
+++ b/servers/slapd/back-ndb/bind.cpp
@@ -0,0 +1,163 @@
+/* bind.cpp - ndb backend bind routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "back-ndb.h"
+
+extern "C" int
+ndb_back_bind( Operation *op, SlapReply *rs )
+{
+	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+	Entry		e = {0};
+	Attribute	*a;
+
+	AttributeDescription *password = slap_schema.si_ad_userPassword;
+
+	NdbArgs NA;
+
+	Debug( LDAP_DEBUG_ARGS,
+		"==> " LDAP_XSTRING(ndb_back_bind) ": dn: %s\n",
+		op->o_req_dn.bv_val, 0, 0);
+
+	/* allow noauth binds */
+	switch ( be_rootdn_bind( op, NULL ) ) {
+	case SLAP_CB_CONTINUE:
+		break;
+
+	default:
+		return rs->sr_err;
+	}
+
+	/* Get our NDB handle */
+	rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+
+	e.e_name = op->o_req_dn;
+	e.e_nname = op->o_req_ndn;
+	NA.e = &e;
+
+dn2entry_retry:
+	NA.txn = NA.ndb->startTransaction();
+	rs->sr_text = NULL;
+	if( !NA.txn ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_bind) ": startTransaction failed: %s (%d)\n",
+			NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto done;
+	}
+
+	/* get entry */
+	{
+		NdbRdns rdns;
+		rdns.nr_num = 0;
+		NA.rdns = &rdns;
+		NA.ocs = NULL;
+		rs->sr_err = ndb_entry_get_info( op, &NA, 0, NULL );
+	}
+	switch(rs->sr_err) {
+	case 0:
+		break;
+	case LDAP_NO_SUCH_OBJECT:
+		rs->sr_err = LDAP_INVALID_CREDENTIALS;
+		goto done;
+	case LDAP_BUSY:
+		rs->sr_text = "ldap_server_busy";
+		goto done;
+#if 0
+	case DB_LOCK_DEADLOCK:
+	case DB_LOCK_NOTGRANTED:
+		goto dn2entry_retry;
+#endif
+	default:
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto done;
+	}
+
+	rs->sr_err = ndb_entry_get_data( op, &NA, 0 );
+	ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+	ber_dupbv( &op->oq_bind.rb_edn, &e.e_name );
+
+	/* check for deleted */
+	if ( is_entry_subentry( &e ) ) {
+		/* entry is an subentry, don't allow bind */
+		Debug( LDAP_DEBUG_TRACE, "entry is subentry\n", 0,
+			0, 0 );
+		rs->sr_err = LDAP_INVALID_CREDENTIALS;
+		goto done;
+	}
+
+	if ( is_entry_alias( &e ) ) {
+		/* entry is an alias, don't allow bind */
+		Debug( LDAP_DEBUG_TRACE, "entry is alias\n", 0, 0, 0 );
+		rs->sr_err = LDAP_INVALID_CREDENTIALS;
+		goto done;
+	}
+
+	if ( is_entry_referral( &e ) ) {
+		Debug( LDAP_DEBUG_TRACE, "entry is referral\n", 0,
+			0, 0 );
+		rs->sr_err = LDAP_INVALID_CREDENTIALS;
+		goto done;
+	}
+
+	switch ( op->oq_bind.rb_method ) {
+	case LDAP_AUTH_SIMPLE:
+		a = attr_find( e.e_attrs, password );
+		if ( a == NULL ) {
+			rs->sr_err = LDAP_INVALID_CREDENTIALS;
+			goto done;
+		}
+
+		if ( slap_passwd_check( op, &e, a, &op->oq_bind.rb_cred,
+					&rs->sr_text ) != 0 )
+		{
+			/* failure; stop front end from sending result */
+			rs->sr_err = LDAP_INVALID_CREDENTIALS;
+			goto done;
+		}
+			
+		rs->sr_err = 0;
+		break;
+
+	default:
+		assert( 0 ); /* should not be reachable */
+		rs->sr_err = LDAP_STRONG_AUTH_NOT_SUPPORTED;
+		rs->sr_text = "authentication method not supported";
+	}
+
+done:
+	NA.txn->close();
+	if ( e.e_attrs ) {
+		attrs_free( e.e_attrs );
+		e.e_attrs = NULL;
+	}
+	if ( rs->sr_err ) {
+		send_ldap_result( op, rs );
+	}
+	/* front end will send result on success (rs->sr_err==0) */
+	return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/compare.cpp b/servers/slapd/back-ndb/compare.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9be410a55989d1fc0a0c202d43260d0d312b2a5b
--- /dev/null
+++ b/servers/slapd/back-ndb/compare.cpp
@@ -0,0 +1,169 @@
+/* compare.cpp - ndb backend compare routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-ndb.h"
+
+int
+ndb_back_compare( Operation *op, SlapReply *rs )
+{
+	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+	Entry		e = {0};
+	Attribute	*a;
+	int		manageDSAit = get_manageDSAit( op );
+
+	NdbArgs NA;
+	NdbRdns rdns;
+	struct berval matched;
+
+	/* Get our NDB handle */
+	rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+
+	rdns.nr_num = 0;
+	NA.rdns = &rdns;
+	e.e_name = op->o_req_dn;
+	e.e_nname = op->o_req_ndn;
+	NA.e = &e;
+
+dn2entry_retry:
+	NA.txn = NA.ndb->startTransaction();
+	rs->sr_text = NULL;
+	if( !NA.txn ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_compare) ": startTransaction failed: %s (%d)\n",
+			NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+
+	NA.ocs = NULL;
+	/* get entry */
+	rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
+	switch( rs->sr_err ) {
+	case 0:
+		break;
+	case LDAP_NO_SUCH_OBJECT:
+		rs->sr_matched = matched.bv_val;
+		if ( NA.ocs )
+			ndb_check_referral( op, rs, &NA );
+		goto return_results;
+	case LDAP_BUSY:
+		rs->sr_text = "ldap server busy";
+		goto return_results;
+#if 0
+	case DB_LOCK_DEADLOCK:
+	case DB_LOCK_NOTGRANTED:
+		goto dn2entry_retry;
+#endif
+	default:
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+
+	rs->sr_err = ndb_entry_get_data( op, &NA, 0 );
+	ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+	if (!manageDSAit && is_entry_referral( &e ) ) {
+		/* return referral only if "disclose" is granted on the object */
+		if ( !access_allowed( op, &e, slap_schema.si_ad_entry,
+			NULL, ACL_DISCLOSE, NULL ) )
+		{
+			rs->sr_err = LDAP_NO_SUCH_OBJECT;
+		} else {
+			/* entry is a referral, don't allow compare */
+			rs->sr_ref = get_entry_referrals( op, &e );
+			rs->sr_err = LDAP_REFERRAL;
+			rs->sr_matched = e.e_name.bv_val;
+			rs->sr_flags |= REP_REF_MUSTBEFREED;
+		}
+
+		Debug( LDAP_DEBUG_TRACE, "entry is referral\n", 0, 0, 0 );
+		goto return_results;
+	}
+
+	if ( get_assert( op ) &&
+		( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
+	{
+		if ( !access_allowed( op, &e, slap_schema.si_ad_entry,
+			NULL, ACL_DISCLOSE, NULL ) )
+		{
+			rs->sr_err = LDAP_NO_SUCH_OBJECT;
+		} else {
+			rs->sr_err = LDAP_ASSERTION_FAILED;
+		}
+		goto return_results;
+	}
+
+	if ( !access_allowed( op, &e, op->oq_compare.rs_ava->aa_desc,
+		&op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL ) )
+	{
+		/* return error only if "disclose"
+		 * is granted on the object */
+		if ( !access_allowed( op, &e, slap_schema.si_ad_entry,
+					NULL, ACL_DISCLOSE, NULL ) )
+		{
+			rs->sr_err = LDAP_NO_SUCH_OBJECT;
+		} else {
+			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+		}
+		goto return_results;
+	}
+
+	rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
+
+	for ( a = attrs_find( e.e_attrs, op->oq_compare.rs_ava->aa_desc );
+		a != NULL;
+		a = attrs_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) )
+	{
+		rs->sr_err = LDAP_COMPARE_FALSE;
+
+		if ( attr_valfind( a,
+			SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+				SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+			&op->oq_compare.rs_ava->aa_value, NULL,
+			op->o_tmpmemctx ) == 0 )
+		{
+			rs->sr_err = LDAP_COMPARE_TRUE;
+			break;
+		}
+	}
+
+return_results:
+	NA.txn->close();
+	if ( e.e_attrs ) {
+		attrs_free( e.e_attrs );
+		e.e_attrs = NULL;
+	}
+	send_ldap_result( op, rs );
+
+	switch ( rs->sr_err ) {
+	case LDAP_COMPARE_FALSE:
+	case LDAP_COMPARE_TRUE:
+		rs->sr_err = LDAP_SUCCESS;
+		break;
+	}
+
+	return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/config.cpp b/servers/slapd/back-ndb/config.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c0fa2036e460cf4772c7fc27f79c611d2196a2fa
--- /dev/null
+++ b/servers/slapd/back-ndb/config.cpp
@@ -0,0 +1,333 @@
+/* config.cpp - ndb backend configuration file routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+#include "lutil.h"
+
+#include "back-ndb.h"
+
+#include "config.h"
+
+extern "C" {
+	static ConfigDriver ndb_cf_gen;
+};
+
+enum {
+	NDB_ATLEN = 1,
+	NDB_ATSET,
+	NDB_INDEX,
+	NDB_ATBLOB
+};
+
+static ConfigTable ndbcfg[] = {
+	{ "dbhost", "hostname", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+		(void *)offsetof(struct ndb_info, ni_hostname),
+		"( OLcfgDbAt:6.1 NAME 'olcDbHost' "
+			"DESC 'Hostname of SQL server' "
+			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+	{ "dbname", "name", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+		(void *)offsetof(struct ndb_info, ni_dbname),
+		"( OLcfgDbAt:6.2 NAME 'olcDbName' "
+			"DESC 'Name of SQL database' "
+			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+	{ "dbuser", "username", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+		(void *)offsetof(struct ndb_info, ni_username),
+		"( OLcfgDbAt:6.3 NAME 'olcDbUser' "
+			"DESC 'Username for SQL session' "
+			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+	{ "dbpass", "password", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+		(void *)offsetof(struct ndb_info, ni_password),
+		"( OLcfgDbAt:6.4 NAME 'olcDbPass' "
+			"DESC 'Password for SQL session' "
+			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+	{ "dbport", "port", 2, 2, 0, ARG_UINT|ARG_OFFSET,
+		(void *)offsetof(struct ndb_info, ni_port),
+		"( OLcfgDbAt:6.5 NAME 'olcDbPort' "
+			"DESC 'Port number of SQL server' "
+			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+	{ "dbsocket", "path", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+		(void *)offsetof(struct ndb_info, ni_socket),
+		"( OLcfgDbAt:6.6 NAME 'olcDbSocket' "
+			"DESC 'Local socket path of SQL server' "
+			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+	{ "dbflag", "flag", 2, 2, 0, ARG_LONG|ARG_OFFSET,
+		(void *)offsetof(struct ndb_info, ni_clflag),
+		"( OLcfgDbAt:6.7 NAME 'olcDbFlag' "
+			"DESC 'Flags for SQL session' "
+			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+	{ "dbconnect", "hostname", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+		(void *)offsetof(struct ndb_info, ni_connectstr),
+		"( OLcfgDbAt:6.8 NAME 'olcDbConnect' "
+			"DESC 'Hostname of NDB server' "
+			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+	{ "dbconnections", "number", 2, 2, 0, ARG_INT|ARG_OFFSET,
+		(void *)offsetof(struct ndb_info, ni_nconns),
+		"( OLcfgDbAt:6.9 NAME 'olcDbConnections' "
+			"DESC 'Number of cluster connections to open' "
+			"SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+	{ "attrlen", "attr> <len", 3, 3, 0, ARG_MAGIC|NDB_ATLEN,
+		(void *)ndb_cf_gen,
+		"( OLcfgDbAt:6.10 NAME 'olcNdbAttrLen' "
+			"DESC 'Column length of a specific attribute' "
+			"EQUALITY caseIgnoreMatch "
+			"SYNTAX OMsDirectoryString )", NULL, NULL },
+	{ "attrset", "set> <attrs", 3, 3, 0, ARG_MAGIC|NDB_ATSET,
+		(void *)ndb_cf_gen,
+		"( OLcfgDbAt:6.11 NAME 'olcNdbAttrSet' "
+			"DESC 'Set of common attributes' "
+			"EQUALITY caseIgnoreMatch "
+			"SYNTAX OMsDirectoryString )", NULL, NULL },
+	{ "index", "attr", 2, 2, 0, ARG_MAGIC|NDB_INDEX,
+		(void *)ndb_cf_gen, "( OLcfgDbAt:0.2 NAME 'olcDbIndex' "
+		"DESC 'Attribute to index' "
+		"EQUALITY caseIgnoreMatch "
+		"SYNTAX OMsDirectoryString )", NULL, NULL },
+	{ "attrblob", "attr", 2, 2, 0, ARG_MAGIC|NDB_ATBLOB,
+		(void *)ndb_cf_gen, "( OLcfgDbAt:6.12 NAME 'olcNdbAttrBlob' "
+		"DESC 'Attribute to treat as a BLOB' "
+		"EQUALITY caseIgnoreMatch "
+		"SYNTAX OMsDirectoryString )", NULL, NULL },
+	{ "directory", "dir", 2, 2, 0, ARG_IGNORED,
+		NULL, "( OLcfgDbAt:0.1 NAME 'olcDbDirectory' "
+			"DESC 'Dummy keyword' "
+			"SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+	{ NULL, NULL, 0, 0, 0, ARG_IGNORED,
+		NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs ndbocs[] = {
+	{
+		"( OLcfgDbOc:6.2 "
+		"NAME 'olcNdbConfig' "
+		"DESC 'NDB backend configuration' "
+		"SUP olcDatabaseConfig "
+		"MUST ( olcDbHost $ olcDbName $ olcDbConnect ) "
+		"MAY ( olcDbUser $ olcDbPass $ olcDbPort $ olcDbSocket $ "
+		"olcDbFlag $ olcDbConnections $ olcNdbAttrLen $ "
+		"olcDbIndex $ olcNdbAttrSet $ olcNdbAttrBlob ) )",
+			Cft_Database, ndbcfg },
+	{ NULL, Cft_Abstract, NULL }
+};
+
+static int
+ndb_cf_gen( ConfigArgs *c )
+{
+	struct ndb_info *ni = (struct ndb_info *)c->be->be_private;
+	int i, rc;
+	NdbAttrInfo *ai;
+	NdbOcInfo *oci;
+	ListNode *ln, **l2;
+	struct berval bv, *bva;
+
+	if ( c->op == SLAP_CONFIG_EMIT ) {
+		char buf[BUFSIZ];
+		rc = 0;
+		bv.bv_val = buf;
+		switch( c->type ) {
+		case NDB_ATLEN:
+			if ( ni->ni_attrlens ) {
+				for ( ln = ni->ni_attrlens; ln; ln=ln->ln_next ) {
+					ai = (NdbAttrInfo *)ln->ln_data;
+					bv.bv_len = snprintf( buf, sizeof(buf),
+						"%s %d", ai->na_name.bv_val,
+							ai->na_len );
+					value_add_one( &c->rvalue_vals, &bv );
+				}
+			} else {
+				rc = 1;
+			}
+			break;
+
+		case NDB_ATSET:
+			if ( ni->ni_attrsets ) {
+				char *ptr, *end = buf+sizeof(buf);
+				for ( ln = ni->ni_attrsets; ln; ln=ln->ln_next ) {
+					oci = (NdbOcInfo *)ln->ln_data;
+					ptr = lutil_strcopy( buf, oci->no_name.bv_val );
+					*ptr++ = ' ';
+					for ( i=0; i<oci->no_nattrs; i++ ) {
+						if ( end - ptr < oci->no_attrs[i]->na_name.bv_len+1 )
+							break;
+						if ( i )
+							*ptr++ = ',';
+						ptr = lutil_strcopy(ptr,
+							oci->no_attrs[i]->na_name.bv_val );
+					}
+					bv.bv_len = ptr - buf;
+					value_add_one( &c->rvalue_vals, &bv );
+				}
+			} else {
+				rc = 1;
+			}
+			break;
+
+		case NDB_INDEX:
+			if ( ni->ni_attridxs ) {
+				for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
+					ai = (NdbAttrInfo *)ln->ln_data;
+					value_add_one( &c->rvalue_vals, &ai->na_name );
+				}
+			} else {
+				rc = 1;
+			}
+			break;
+
+		case NDB_ATBLOB:
+			if ( ni->ni_attrblobs ) {
+				for ( ln = ni->ni_attrblobs; ln; ln=ln->ln_next ) {
+					ai = (NdbAttrInfo *)ln->ln_data;
+					value_add_one( &c->rvalue_vals, &ai->na_name );
+				}
+			} else {
+				rc = 1;
+			}
+			break;
+
+		}
+		return rc;
+	} else if ( c->op == LDAP_MOD_DELETE ) { /* FIXME */
+		rc = 0;
+		switch( c->type ) {
+		case NDB_INDEX:
+			if ( c->valx == -1 ) {
+
+				/* delete all */
+
+			} else {
+
+			}
+			break;
+		}
+		return rc;
+	}
+
+	switch( c->type ) {
+	case NDB_ATLEN:
+		ber_str2bv( c->argv[1], 0, 0, &bv );
+		ai = ndb_ai_get( ni, &bv );
+		if ( !ai ) {
+			snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid attr %s",
+				c->log, c->argv[1] );
+			Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+			return -1;
+		}
+		for ( ln = ni->ni_attrlens; ln; ln = ln->ln_next ) {
+			if ( ln->ln_data == (void *)ai ) {
+				snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: attr len already set for %s",
+					c->log, c->argv[1] );
+				Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+				return -1;
+			}
+		}
+		ai->na_len = atoi( c->argv[2] );
+		ai->na_flag |= NDB_INFO_ATLEN;
+		ln = (ListNode *)ch_malloc( sizeof(ListNode));
+		ln->ln_data = ai;
+		ln->ln_next = NULL;
+		for ( l2 = &ni->ni_attrlens; *l2; l2 = &(*l2)->ln_next );
+		*l2 = ln;
+		break;
+		
+	case NDB_INDEX:
+		ber_str2bv( c->argv[1], 0, 0, &bv );
+		ai = ndb_ai_get( ni, &bv );
+		if ( !ai ) {
+			snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid attr %s",
+				c->log, c->argv[1] );
+			Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+			return -1;
+		}
+		for ( ln = ni->ni_attridxs; ln; ln = ln->ln_next ) {
+			if ( ln->ln_data == (void *)ai ) {
+				snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: attr index already set for %s",
+					c->log, c->argv[1] );
+				Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+				return -1;
+			}
+		}
+		ai->na_flag |= NDB_INFO_INDEX;
+		ln = (ListNode *)ch_malloc( sizeof(ListNode));
+		ln->ln_data = ai;
+		ln->ln_next = NULL;
+		for ( l2 = &ni->ni_attridxs; *l2; l2 = &(*l2)->ln_next );
+		*l2 = ln;
+		break;
+
+	case NDB_ATSET:
+		ber_str2bv( c->argv[1], 0, 0, &bv );
+		bva = ndb_str2bvarray( c->argv[2], strlen( c->argv[2] ), ',', NULL );
+		rc = ndb_aset_get( ni, &bv, bva, &oci );
+		ber_bvarray_free( bva );
+		if ( rc ) {
+			if ( rc == LDAP_ALREADY_EXISTS ) {
+				snprintf( c->cr_msg, sizeof( c->cr_msg ),
+					"%s: attrset %s already defined",
+					c->log, c->argv[1] );
+			} else {
+				snprintf( c->cr_msg, sizeof( c->cr_msg ),
+					"%s: invalid attrset %s (%d)",
+					c->log, c->argv[1], rc );
+			}
+			Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+			return -1;
+		}
+		ln = (ListNode *)ch_malloc( sizeof(ListNode));
+		ln->ln_data = oci;
+		ln->ln_next = NULL;
+		for ( l2 = &ni->ni_attrsets; *l2; l2 = &(*l2)->ln_next );
+		*l2 = ln;
+		break;
+
+	case NDB_ATBLOB:
+		ber_str2bv( c->argv[1], 0, 0, &bv );
+		ai = ndb_ai_get( ni, &bv );
+		if ( !ai ) {
+			snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid attr %s",
+				c->log, c->argv[1] );
+			Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+			return -1;
+		}
+		for ( ln = ni->ni_attrblobs; ln; ln = ln->ln_next ) {
+			if ( ln->ln_data == (void *)ai ) {
+				snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: attr blob already set for %s",
+					c->log, c->argv[1] );
+				Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+				return -1;
+			}
+		}
+		ai->na_flag |= NDB_INFO_ATBLOB;
+		ln = (ListNode *)ch_malloc( sizeof(ListNode));
+		ln->ln_data = ai;
+		ln->ln_next = NULL;
+		for ( l2 = &ni->ni_attrblobs; *l2; l2 = &(*l2)->ln_next );
+		*l2 = ln;
+		break;
+
+	}
+	return 0;
+}
+
+extern "C"
+int ndb_back_init_cf( BackendInfo *bi )
+{
+	bi->bi_cf_ocs = ndbocs;
+
+	return config_register_schema( ndbcfg, ndbocs );
+}
diff --git a/servers/slapd/back-ndb/delete.cpp b/servers/slapd/back-ndb/delete.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..599f20ca12256f79d7003c15886f3f133f2c33b9
--- /dev/null
+++ b/servers/slapd/back-ndb/delete.cpp
@@ -0,0 +1,322 @@
+/* delete.cpp - ndb backend delete routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "lutil.h"
+#include "back-ndb.h"
+
+static struct berval glue_bv = BER_BVC("glue");
+
+int
+ndb_back_delete( Operation *op, SlapReply *rs )
+{
+	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+	Entry	e = {0};
+	Entry	p = {0};
+	int		manageDSAit = get_manageDSAit( op );
+	AttributeDescription *children = slap_schema.si_ad_children;
+	AttributeDescription *entry = slap_schema.si_ad_entry;
+
+	NdbArgs NA;
+	NdbRdns rdns;
+	struct berval matched;
+
+	int	num_retries = 0;
+
+	int     rc;
+
+	LDAPControl **preread_ctrl = NULL;
+	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+	int num_ctrls = 0;
+
+	Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(ndb_back_delete) ": %s\n",
+		op->o_req_dn.bv_val, 0, 0 );
+
+	ctrls[num_ctrls] = 0;
+
+	/* allocate CSN */
+	if ( BER_BVISNULL( &op->o_csn ) ) {
+		struct berval csn;
+		char csnbuf[LDAP_LUTIL_CSNSTR_BUFSIZE];
+
+		csn.bv_val = csnbuf;
+		csn.bv_len = sizeof(csnbuf);
+		slap_get_csn( op, &csn, 1 );
+	}
+
+	if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
+		dnParent( &op->o_req_dn, &p.e_name );
+		dnParent( &op->o_req_ndn, &p.e_nname );
+	}
+
+	/* Get our NDB handle */
+	rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+	rdns.nr_num = 0;
+	NA.rdns = &rdns;
+	NA.ocs = NULL;
+	NA.e = &e;
+	e.e_name = op->o_req_dn;
+	e.e_nname = op->o_req_ndn;
+
+	if( 0 ) {
+retry:	/* transaction retry */
+		NA.txn->close();
+		NA.txn = NULL;
+		Debug( LDAP_DEBUG_TRACE,
+			"==> " LDAP_XSTRING(ndb_back_delete) ": retrying...\n",
+			0, 0, 0 );
+		if ( op->o_abandon ) {
+			rs->sr_err = SLAPD_ABANDON;
+			goto return_results;
+		}
+		if ( NA.ocs ) {
+			ber_bvarray_free( NA.ocs );
+			NA.ocs = NULL;
+		}
+		ndb_trans_backoff( ++num_retries );
+	}
+
+	/* begin transaction */
+	NA.txn = NA.ndb->startTransaction();
+	rs->sr_text = NULL;
+	if( !NA.txn ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_delete) ": startTransaction failed: %s (%d)\n",
+			NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+
+	/* get entry */
+	rs->sr_err = ndb_entry_get_info( op, &NA, 1, &matched );
+	switch( rs->sr_err ) {
+	case 0:
+	case LDAP_NO_SUCH_OBJECT:
+		break;
+#if 0
+	case DB_LOCK_DEADLOCK:
+	case DB_LOCK_NOTGRANTED:
+		goto retry;
+#endif
+	case LDAP_BUSY:
+		rs->sr_text = "ldap server busy";
+		goto return_results;
+	default:
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+
+	if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ||
+		( !manageDSAit && bvmatch( NA.ocs, &glue_bv ))) {
+		Debug( LDAP_DEBUG_ARGS,
+			"<=- " LDAP_XSTRING(ndb_back_delete) ": no such object %s\n",
+			op->o_req_dn.bv_val, 0, 0);
+
+		if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+			rs->sr_matched = matched.bv_val;
+			if ( NA.ocs )
+				ndb_check_referral( op, rs, &NA );
+		} else {
+			rs->sr_matched = p.e_name.bv_val;
+			rs->sr_err = LDAP_NO_SUCH_OBJECT;
+		}
+		goto return_results;
+	}
+
+	/* check parent for "children" acl */
+	rs->sr_err = access_allowed( op, &p,
+		children, NULL, ACL_WDEL, NULL );
+
+	if ( !rs->sr_err  ) {
+		Debug( LDAP_DEBUG_TRACE,
+			"<=- " LDAP_XSTRING(ndb_back_delete) ": no write "
+			"access to parent\n", 0, 0, 0 );
+		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+		rs->sr_text = "no write access to parent";
+		goto return_results;
+	}
+
+	rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
+
+	rs->sr_err = access_allowed( op, &e,
+		entry, NULL, ACL_WDEL, NULL );
+
+	if ( !rs->sr_err  ) {
+		Debug( LDAP_DEBUG_TRACE,
+			"<=- " LDAP_XSTRING(ndb_back_delete) ": no write access "
+			"to entry\n", 0, 0, 0 );
+		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+		rs->sr_text = "no write access to entry";
+		goto return_results;
+	}
+
+	if ( !manageDSAit && is_entry_referral( &e ) ) {
+		/* entry is a referral, don't allow delete */
+		rs->sr_ref = get_entry_referrals( op, &e );
+
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_delete) ": entry is referral\n",
+			0, 0, 0 );
+
+		rs->sr_err = LDAP_REFERRAL;
+		rs->sr_matched = e.e_name.bv_val;
+		rs->sr_flags = REP_REF_MUSTBEFREED;
+		goto return_results;
+	}
+
+	if ( get_assert( op ) &&
+		( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
+	{
+		rs->sr_err = LDAP_ASSERTION_FAILED;
+		goto return_results;
+	}
+
+	/* pre-read */
+	if( op->o_preread ) {
+		if( preread_ctrl == NULL ) {
+			preread_ctrl = &ctrls[num_ctrls++];
+			ctrls[num_ctrls] = NULL;
+		}
+		if( slap_read_controls( op, rs, &e,
+			&slap_pre_read_bv, preread_ctrl ) )
+		{
+			Debug( LDAP_DEBUG_TRACE,
+				"<=- " LDAP_XSTRING(ndb_back_delete) ": pre-read "
+				"failed!\n", 0, 0, 0 );
+			if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+				/* FIXME: is it correct to abort
+				 * operation if control fails? */
+				goto return_results;
+			}
+		}
+	}
+
+	/* Can't do it if we have kids */
+	rs->sr_err = ndb_has_children( &NA, &rc );
+	if ( rs->sr_err ) {
+		Debug(LDAP_DEBUG_ARGS,
+			"<=- " LDAP_XSTRING(ndb_back_delete)
+			": has_children failed: %s (%d)\n",
+			NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+	if ( rc == LDAP_COMPARE_TRUE ) {
+		Debug(LDAP_DEBUG_ARGS,
+			"<=- " LDAP_XSTRING(ndb_back_delete)
+			": non-leaf %s\n",
+			op->o_req_dn.bv_val, 0, 0);
+		rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+		rs->sr_text = "subordinate objects must be deleted first";
+		goto return_results;
+	}
+
+	/* delete info */
+	rs->sr_err = ndb_entry_del_info( op->o_bd, &NA );
+	if ( rs->sr_err != 0 ) {
+		Debug(LDAP_DEBUG_TRACE,
+			"<=- " LDAP_XSTRING(ndb_back_delete) ": del_info failed: %s (%d)\n",
+			NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+		rs->sr_text = "DN index delete failed";
+		rs->sr_err = LDAP_OTHER;
+		goto return_results;
+	}
+
+	/* delete data */
+	rs->sr_err = ndb_entry_del_data( op->o_bd, &NA );
+	if ( rs->sr_err != 0 ) {
+		Debug( LDAP_DEBUG_TRACE,
+			"<=- " LDAP_XSTRING(ndb_back_delete) ": del_data failed: %s (%d)\n",
+			NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+		rs->sr_text = "entry delete failed";
+		rs->sr_err = LDAP_OTHER;
+		goto return_results;
+	}
+
+	if( op->o_noop ) {
+		if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
+			NdbOperation::AbortOnError, 1 )) != 0 ) {
+			rs->sr_text = "txn (no-op) failed";
+		} else {
+			rs->sr_err = LDAP_X_NO_OPERATION;
+		}
+	} else {
+		if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
+			NdbOperation::AbortOnError, 1 )) != 0 ) {
+			rs->sr_text = "txn_commit failed";
+		} else {
+			rs->sr_err = LDAP_SUCCESS;
+		}
+	}
+
+	if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_delete) ": txn_%s failed: %s (%d)\n",
+			op->o_noop ? "abort (no-op)" : "commit",
+			NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "commit failed";
+
+		goto return_results;
+	}
+	NA.txn->close();
+	NA.txn = NULL;
+
+	Debug( LDAP_DEBUG_TRACE,
+		LDAP_XSTRING(ndb_back_delete) ": deleted%s id=%08lx dn=\"%s\"\n",
+		op->o_noop ? " (no-op)" : "",
+		e.e_id, op->o_req_dn.bv_val );
+	rs->sr_err = LDAP_SUCCESS;
+	rs->sr_text = NULL;
+	if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+	if ( NA.ocs ) {
+		ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+		NA.ocs = NULL;
+	}
+
+	/* free entry */
+	if( e.e_attrs != NULL ) {
+		attrs_free( e.e_attrs );
+		e.e_attrs = NULL;
+	}
+
+	if( NA.txn != NULL ) {
+		NA.txn->execute( Rollback );
+		NA.txn->close();
+	}
+
+	send_ldap_result( op, rs );
+	slap_graduate_commit_csn( op );
+
+	if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+		slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+		slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+	}
+	return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/init.cpp b/servers/slapd/back-ndb/init.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..77e4d13f23b1b55c86ddbf0ee38903b3647dae14
--- /dev/null
+++ b/servers/slapd/back-ndb/init.cpp
@@ -0,0 +1,451 @@
+/* init.cpp - initialize ndb backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <sys/stat.h>
+#include "back-ndb.h"
+#include <lutil.h>
+#include "config.h"
+
+extern "C" {
+	static BI_db_init ndb_db_init;
+	static BI_db_close ndb_db_close;
+	static BI_db_open ndb_db_open;
+	static BI_db_destroy ndb_db_destroy;
+}
+
+static struct berval ndb_optable = BER_BVC("OL_opattrs");
+
+static struct berval ndb_opattrs[] = {
+	BER_BVC("structuralObjectClass"),
+	BER_BVC("entryUUID"),
+	BER_BVC("creatorsName"),
+	BER_BVC("createTimestamp"),
+	BER_BVC("entryCSN"),
+	BER_BVC("modifiersName"),
+	BER_BVC("modifyTimestamp"),
+	BER_BVNULL
+};
+
+static int ndb_oplens[] = {
+	0,	/* structuralOC, default */
+	36,	/* entryUUID */
+	0,	/* creatorsName, default */
+	26,	/* createTimestamp */
+	40,	/* entryCSN */
+	0,	/* modifiersName, default */
+	26,	/* modifyTimestamp */
+	-1
+};
+
+static Uint32 ndb_lastrow[1];
+NdbInterpretedCode *ndb_lastrow_code;
+
+static int
+ndb_db_init( BackendDB *be, ConfigReply *cr )
+{
+	struct ndb_info	*ni;
+	int rc = 0;
+
+	Debug( LDAP_DEBUG_TRACE,
+		LDAP_XSTRING(ndb_db_init) ": Initializing ndb database\n",
+		0, 0, 0 );
+
+	/* allocate backend-database-specific stuff */
+	ni = (struct ndb_info *) ch_calloc( 1, sizeof(struct ndb_info) );
+
+	be->be_private = ni;
+	be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+	ni->ni_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
+
+	ldap_pvt_thread_rdwr_init( &ni->ni_ai_rwlock );
+	ldap_pvt_thread_rdwr_init( &ni->ni_oc_rwlock );
+	ldap_pvt_thread_mutex_init( &ni->ni_conn_mutex );
+
+#ifdef DO_MONITORING
+	rc = ndb_monitor_db_init( be );
+#endif
+
+	return rc;
+}
+
+static int
+ndb_db_close( BackendDB *be, ConfigReply *cr );
+
+static int
+ndb_db_open( BackendDB *be, ConfigReply *cr )
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+	char sqlbuf[BUFSIZ], *ptr;
+	int rc, i;
+
+	if ( be->be_suffix == NULL ) {
+		snprintf( cr->msg, sizeof( cr->msg ),
+			"ndb_db_open: need suffix" );
+		Debug( LDAP_DEBUG_ANY, "%s\n",
+			cr->msg, 0, 0 );
+		return -1;
+	}
+
+	Debug( LDAP_DEBUG_ARGS,
+		LDAP_XSTRING(ndb_db_open) ": \"%s\"\n",
+		be->be_suffix[0].bv_val, 0, 0 );
+
+	if ( ni->ni_nconns < 1 )
+		ni->ni_nconns = 1;
+
+	ni->ni_cluster = (Ndb_cluster_connection **)ch_calloc( ni->ni_nconns, sizeof( Ndb_cluster_connection *));
+	for ( i=0; i<ni->ni_nconns; i++ ) {
+		ni->ni_cluster[i] = new Ndb_cluster_connection( ni->ni_connectstr );
+		rc = ni->ni_cluster[i]->connect( 20, 5, 1 );
+		if ( rc ) {
+			snprintf( cr->msg, sizeof( cr->msg ),
+				"ndb_db_open: ni_cluster[%d]->connect failed (%d)",
+				i, rc );
+			goto fail;
+		}
+	}
+	for ( i=0; i<ni->ni_nconns; i++ ) {
+		rc = ni->ni_cluster[i]->wait_until_ready( 30, 0 );
+		if ( rc ) {
+			snprintf( cr->msg, sizeof( cr->msg ),
+				"ndb_db_open: ni_cluster[%d]->wait failed (%d)",
+				i, rc );
+			goto fail;
+		}
+	}
+
+	mysql_init( &ni->ni_sql );
+	if ( !mysql_real_connect( &ni->ni_sql, ni->ni_hostname, ni->ni_username, ni->ni_password,
+		"", ni->ni_port, ni->ni_socket, ni->ni_clflag )) {
+		snprintf( cr->msg, sizeof( cr->msg ),
+			"ndb_db_open: mysql_real_connect failed, %s (%d)",
+			mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+		rc = -1;
+		goto fail;
+	}
+
+	sprintf( sqlbuf, "CREATE DATABASE IF NOT EXISTS %s", ni->ni_dbname );
+	rc = mysql_query( &ni->ni_sql, sqlbuf );
+	if ( rc ) {
+		snprintf( cr->msg, sizeof( cr->msg ),
+			"ndb_db_open: CREATE DATABASE %s failed, %s (%d)",
+			ni->ni_dbname, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+		goto fail;
+	}
+
+	sprintf( sqlbuf, "USE %s", ni->ni_dbname );
+	rc = mysql_query( &ni->ni_sql, sqlbuf );
+	if ( rc ) {
+		snprintf( cr->msg, sizeof( cr->msg ),
+			"ndb_db_open: USE DATABASE %s failed, %s (%d)",
+			ni->ni_dbname, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+		goto fail;
+	}
+
+	ptr = sqlbuf;
+	ptr += sprintf( ptr, "CREATE TABLE IF NOT EXISTS " DN2ID_TABLE " ("
+		"eid bigint unsigned NOT NULL, "
+		"object_classes VARCHAR(1024) NOT NULL, "
+		"a0 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a1 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a2 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a3 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a4 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a5 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a6 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a7 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a8 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a9 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a10 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a11 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a12 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a13 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a14 VARCHAR(128) NOT NULL DEFAULT '', "
+		"a15 VARCHAR(128) NOT NULL DEFAULT '', "
+		"PRIMARY KEY (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15), "
+		"UNIQUE KEY eid (eid) USING HASH" );
+	/* Create index columns */
+	if ( ni->ni_attridxs ) {
+		ListNode *ln;
+		int newcol = 0;
+
+		*ptr++ = ',';
+		*ptr++ = ' ';
+		for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
+			NdbAttrInfo *ai = (NdbAttrInfo *)ln->ln_data;
+			ptr += sprintf( ptr, "`%s` VARCHAR(%d), ",
+				ai->na_name.bv_val, ai->na_len );
+		}
+		ptr = lutil_strcopy(ptr, "KEY " INDEX_NAME " (" );
+
+		for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
+			NdbAttrInfo *ai = (NdbAttrInfo *)ln->ln_data;
+			if ( newcol ) *ptr++ = ',';
+			*ptr++ = '`';
+			ptr = lutil_strcopy( ptr, ai->na_name.bv_val );
+			*ptr++ = '`';
+			ai->na_ixcol = newcol + 18;
+			newcol++;
+		}
+		*ptr++ = ')';
+	}
+	strcpy( ptr, ") ENGINE=ndb" );
+	rc = mysql_query( &ni->ni_sql, sqlbuf );
+	if ( rc ) {
+		snprintf( cr->msg, sizeof( cr->msg ),
+			"ndb_db_open: CREATE TABLE " DN2ID_TABLE " failed, %s (%d)",
+			mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+		goto fail;
+	}
+
+	rc = mysql_query( &ni->ni_sql, "CREATE TABLE IF NOT EXISTS " NEXTID_TABLE " ("
+		"a bigint unsigned AUTO_INCREMENT PRIMARY KEY ) ENGINE=ndb" );
+	if ( rc ) {
+		snprintf( cr->msg, sizeof( cr->msg ),
+			"ndb_db_open: CREATE TABLE " NEXTID_TABLE " failed, %s (%d)",
+			mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+		goto fail;
+	}
+
+	{
+		NdbOcInfo *oci;
+
+		rc = ndb_aset_get( ni, &ndb_optable, ndb_opattrs, &oci );
+		if ( rc ) {
+			snprintf( cr->msg, sizeof( cr->msg ),
+				"ndb_db_open: ndb_aset_get( %s ) failed (%d)",
+				ndb_optable.bv_val, rc );
+			goto fail;
+		}
+		for ( i=0; ndb_oplens[i] >= 0; i++ ) {
+			if ( ndb_oplens[i] )
+				oci->no_attrs[i]->na_len = ndb_oplens[i];
+		}
+		rc = ndb_aset_create( ni, oci );
+		if ( rc ) {
+			snprintf( cr->msg, sizeof( cr->msg ),
+				"ndb_db_open: ndb_aset_create( %s ) failed (%d)",
+				ndb_optable.bv_val, rc );
+			goto fail;
+		}
+		ni->ni_opattrs = oci;
+	}
+	/* Create attribute sets */
+	{
+		ListNode *ln;
+
+		for ( ln = ni->ni_attrsets; ln; ln=ln->ln_next ) {
+			NdbOcInfo *oci = (NdbOcInfo *)ln->ln_data;
+			rc = ndb_aset_create( ni, oci );
+			if ( rc ) {
+				snprintf( cr->msg, sizeof( cr->msg ),
+					"ndb_db_open: ndb_aset_create( %s ) failed (%d)",
+					oci->no_name.bv_val, rc );
+				goto fail;
+			}
+		}
+	}
+	/* Initialize any currently used objectClasses */
+	{
+		Ndb *ndb;
+		const NdbDictionary::Dictionary *myDict;
+
+		ndb = new Ndb( ni->ni_cluster[0], ni->ni_dbname );
+		ndb->init(1024);
+
+		myDict = ndb->getDictionary();
+		ndb_oc_read( ni, myDict );
+		delete ndb;
+	}
+
+#ifdef DO_MONITORING
+	/* monitor setup */
+	rc = ndb_monitor_db_open( be );
+	if ( rc != 0 ) {
+		goto fail;
+	}
+#endif
+
+	return 0;
+
+fail:
+	Debug( LDAP_DEBUG_ANY, "%s\n",
+		cr->msg, 0, 0 );
+	ndb_db_close( be, NULL );
+	return rc;
+}
+
+static int
+ndb_db_close( BackendDB *be, ConfigReply *cr )
+{
+	int i;
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+
+	mysql_close( &ni->ni_sql );
+	if ( ni->ni_cluster ) {
+		for ( i=0; i<ni->ni_nconns; i++ ) {
+			if ( ni->ni_cluster[i] ) {
+				delete ni->ni_cluster[i];
+				ni->ni_cluster[i] = NULL;
+			}
+		}
+		ch_free( ni->ni_cluster );
+		ni->ni_cluster = NULL;
+	}
+
+#ifdef DO_MONITORING
+	/* monitor handling */
+	(void)ndb_monitor_db_close( be );
+#endif
+
+	return 0;
+}
+
+static int
+ndb_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+
+#ifdef DO_MONITORING
+	/* monitor handling */
+	(void)ndb_monitor_db_destroy( be );
+#endif
+
+	ldap_pvt_thread_mutex_destroy( &ni->ni_conn_mutex );
+	ldap_pvt_thread_rdwr_destroy( &ni->ni_ai_rwlock );
+	ldap_pvt_thread_rdwr_destroy( &ni->ni_oc_rwlock );
+
+	ch_free( ni );
+	be->be_private = NULL;
+
+	return 0;
+}
+
+extern "C" int
+ndb_back_initialize(
+	BackendInfo	*bi )
+{
+	static char *controls[] = {
+		LDAP_CONTROL_ASSERT,
+		LDAP_CONTROL_MANAGEDSAIT,
+		LDAP_CONTROL_NOOP,
+		LDAP_CONTROL_PAGEDRESULTS,
+		LDAP_CONTROL_PRE_READ,
+		LDAP_CONTROL_POST_READ,
+		LDAP_CONTROL_SUBENTRIES,
+		LDAP_CONTROL_X_PERMISSIVE_MODIFY,
+#ifdef LDAP_X_TXN
+		LDAP_CONTROL_X_TXN_SPEC,
+#endif
+		NULL
+	};
+
+	int rc = 0;
+
+	/* initialize the underlying database system */
+	Debug( LDAP_DEBUG_TRACE,
+		LDAP_XSTRING(ndb_back_initialize) ": initialize ndb backend\n", 0, 0, 0 );
+
+	ndb_init();
+
+	ndb_lastrow_code = new NdbInterpretedCode( NULL, ndb_lastrow, 1 );
+	ndb_lastrow_code->interpret_exit_last_row();
+	ndb_lastrow_code->finalise();
+
+	bi->bi_flags |=
+		SLAP_BFLAG_INCREMENT |
+		SLAP_BFLAG_SUBENTRIES |
+		SLAP_BFLAG_ALIASES |
+		SLAP_BFLAG_REFERRALS;
+
+	bi->bi_controls = controls;
+
+	bi->bi_open = 0;
+	bi->bi_close = 0;
+	bi->bi_config = 0;
+	bi->bi_destroy = 0;
+
+	bi->bi_db_init = ndb_db_init;
+	bi->bi_db_config = config_generic_wrapper;
+	bi->bi_db_open = ndb_db_open;
+	bi->bi_db_close = ndb_db_close;
+	bi->bi_db_destroy = ndb_db_destroy;
+
+	bi->bi_op_add = ndb_back_add;
+	bi->bi_op_bind = ndb_back_bind;
+	bi->bi_op_compare = ndb_back_compare;
+	bi->bi_op_delete = ndb_back_delete;
+	bi->bi_op_modify = ndb_back_modify;
+	bi->bi_op_modrdn = ndb_back_modrdn;
+	bi->bi_op_search = ndb_back_search;
+
+	bi->bi_op_unbind = 0;
+
+#if 0
+	bi->bi_extended = ndb_extended;
+
+	bi->bi_chk_referrals = ndb_referrals;
+#endif
+	bi->bi_operational = ndb_operational;
+	bi->bi_has_subordinates = ndb_has_subordinates;
+	bi->bi_entry_release_rw = 0;
+	bi->bi_entry_get_rw = ndb_entry_get;
+
+	/*
+	 * hooks for slap tools
+	 */
+	bi->bi_tool_entry_open = ndb_tool_entry_open;
+	bi->bi_tool_entry_close = ndb_tool_entry_close;
+	bi->bi_tool_entry_first = ndb_tool_entry_first;
+	bi->bi_tool_entry_next = ndb_tool_entry_next;
+	bi->bi_tool_entry_get = ndb_tool_entry_get;
+	bi->bi_tool_entry_put = ndb_tool_entry_put;
+#if 0
+	bi->bi_tool_entry_reindex = ndb_tool_entry_reindex;
+	bi->bi_tool_sync = 0;
+	bi->bi_tool_dn2id_get = ndb_tool_dn2id_get;
+	bi->bi_tool_entry_modify = ndb_tool_entry_modify;
+#endif
+
+	bi->bi_connection_init = 0;
+	bi->bi_connection_destroy = 0;
+
+	rc = ndb_back_init_cf( bi );
+
+	return rc;
+}
+
+#if	SLAPD_NDB == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+extern "C" { int init_module( int argc, char *argv[] ); }
+
+SLAP_BACKEND_INIT_MODULE( ndb )
+
+#endif /* SLAPD_NDB == SLAPD_MOD_DYNAMIC */
+
diff --git a/servers/slapd/back-ndb/modify.cpp b/servers/slapd/back-ndb/modify.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a844dcff41560000d68d3b227fae32dffface75d
--- /dev/null
+++ b/servers/slapd/back-ndb/modify.cpp
@@ -0,0 +1,652 @@
+/* modify.cpp - ndb backend modify routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "back-ndb.h"
+
+/* This is a copy from slapd/mods.c, but with compaction tweaked
+ * to swap values from the tail into deleted slots, to reduce the
+ * overall update traffic.
+ */
+static int
+ndb_modify_delete(
+	Entry	*e,
+	Modification	*mod,
+	int	permissive,
+	const char	**text,
+	char *textbuf, size_t textlen,
+	int *idx )
+{
+	Attribute	*a;
+	MatchingRule 	*mr = mod->sm_desc->ad_type->sat_equality;
+	struct berval *cvals;
+	int		*id2 = NULL;
+	int		i, j, rc = 0, num;
+	unsigned flags;
+	char		dummy = '\0';
+
+	/* For ordered vals, we have no choice but to preserve order */
+	if ( mod->sm_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL )
+		return modify_delete_vindex( e, mod, permissive, text,
+			textbuf, textlen, idx );
+
+	/*
+	 * If permissive is set, then the non-existence of an 
+	 * attribute is not treated as an error.
+	 */
+
+	/* delete the entire attribute */
+	if ( mod->sm_values == NULL ) {
+		rc = attr_delete( &e->e_attrs, mod->sm_desc );
+
+		if( permissive ) {
+			rc = LDAP_SUCCESS;
+		} else if( rc != LDAP_SUCCESS ) {
+			*text = textbuf;
+			snprintf( textbuf, textlen,
+				"modify/delete: %s: no such attribute",
+				mod->sm_desc->ad_cname.bv_val );
+			rc = LDAP_NO_SUCH_ATTRIBUTE;
+		}
+		return rc;
+	}
+
+	/* FIXME: Catch old code that doesn't set sm_numvals.
+	 */
+	if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
+		for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
+		assert( mod->sm_numvals == i );
+	}
+	if ( !idx ) {
+		id2 = (int *)ch_malloc( mod->sm_numvals * sizeof( int ));
+		idx = id2;
+	}
+
+	if( mr == NULL || !mr->smr_match ) {
+		/* disallow specific attributes from being deleted if
+			no equality rule */
+		*text = textbuf;
+		snprintf( textbuf, textlen,
+			"modify/delete: %s: no equality matching rule",
+			mod->sm_desc->ad_cname.bv_val );
+		rc = LDAP_INAPPROPRIATE_MATCHING;
+		goto return_result;
+	}
+
+	/* delete specific values - find the attribute first */
+	if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
+		if( permissive ) {
+			rc = LDAP_SUCCESS;
+			goto return_result;
+		}
+		*text = textbuf;
+		snprintf( textbuf, textlen,
+			"modify/delete: %s: no such attribute",
+			mod->sm_desc->ad_cname.bv_val );
+		rc = LDAP_NO_SUCH_ATTRIBUTE;
+		goto return_result;
+	}
+
+	if ( mod->sm_nvalues ) {
+		flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
+			| SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
+			| SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
+		cvals = mod->sm_nvalues;
+	} else {
+		flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
+		cvals = mod->sm_values;
+	}
+
+	/* Locate values to delete */
+	for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
+		unsigned sort;
+		rc = attr_valfind( a, flags, &cvals[i], &sort, NULL );
+		if ( rc == LDAP_SUCCESS ) {
+			idx[i] = sort;
+		} else if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) {
+			if ( permissive ) {
+				idx[i] = -1;
+				continue;
+			}
+			*text = textbuf;
+			snprintf( textbuf, textlen,
+				"modify/delete: %s: no such value",
+				mod->sm_desc->ad_cname.bv_val );
+			goto return_result;
+		} else {
+			*text = textbuf;
+			snprintf( textbuf, textlen,
+				"modify/delete: %s: matching rule failed",
+				mod->sm_desc->ad_cname.bv_val );
+			goto return_result;
+		}
+	}
+
+	num = a->a_numvals;
+
+	/* Delete the values */
+	for ( i = 0; i < mod->sm_numvals; i++ ) {
+		/* Skip permissive values that weren't found */
+		if ( idx[i] < 0 )
+			continue;
+		/* Skip duplicate delete specs */
+		if ( a->a_vals[idx[i]].bv_val == &dummy )
+			continue;
+		/* delete value and mark it as gone */
+		free( a->a_vals[idx[i]].bv_val );
+		a->a_vals[idx[i]].bv_val = &dummy;
+		if( a->a_nvals != a->a_vals ) {
+			free( a->a_nvals[idx[i]].bv_val );
+			a->a_nvals[idx[i]].bv_val = &dummy;
+		}
+		a->a_numvals--;
+	}
+
+	/* compact array */
+	for ( i=0; i<num; i++ ) {
+		if ( a->a_vals[i].bv_val != &dummy )
+			continue;
+		for ( --num; num > i && a->a_vals[num].bv_val == &dummy; num-- )
+			;
+		a->a_vals[i] = a->a_vals[num];
+		if ( a->a_nvals != a->a_vals )
+			a->a_nvals[i] = a->a_nvals[num];
+	}
+
+	BER_BVZERO( &a->a_vals[num] );
+	if (a->a_nvals != a->a_vals) {
+		BER_BVZERO( &a->a_nvals[num] );
+	}
+
+	/* if no values remain, delete the entire attribute */
+	if ( !a->a_numvals ) {
+		if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
+			/* Can never happen */
+			*text = textbuf;
+			snprintf( textbuf, textlen,
+				"modify/delete: %s: no such attribute",
+				mod->sm_desc->ad_cname.bv_val );
+			rc = LDAP_NO_SUCH_ATTRIBUTE;
+		}
+	}
+return_result:
+	if ( id2 )
+		ch_free( id2 );
+	return rc;
+}
+
+int ndb_modify_internal(
+	Operation *op,
+	NdbArgs *NA,
+	const char **text,
+	char *textbuf,
+	size_t textlen )
+{
+	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+	Modification	*mod;
+	Modifications	*ml;
+	Modifications	*modlist = op->orm_modlist;
+	NdbAttrInfo **modai, *atmp;
+	const NdbDictionary::Dictionary *myDict;
+	const NdbDictionary::Table *myTable;
+	int got_oc = 0, nmods = 0, nai = 0, i, j;
+	int rc, indexed = 0;
+	Attribute *old = NULL;
+
+	Debug( LDAP_DEBUG_TRACE, "ndb_modify_internal: 0x%08lx: %s\n",
+		NA->e->e_id, NA->e->e_dn, 0);
+
+	if ( !acl_check_modlist( op, NA->e, modlist )) {
+		return LDAP_INSUFFICIENT_ACCESS;
+	}
+
+	old = attrs_dup( NA->e->e_attrs );
+
+	for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+		mod = &ml->sml_mod;
+		nmods++;
+
+		switch ( mod->sm_op ) {
+		case LDAP_MOD_ADD:
+			Debug(LDAP_DEBUG_ARGS,
+				"ndb_modify_internal: add %s\n",
+				mod->sm_desc->ad_cname.bv_val, 0, 0);
+			rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
+				text, textbuf, textlen );
+			if( rc != LDAP_SUCCESS ) {
+				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+					rc, *text, 0);
+			}
+			break;
+
+		case LDAP_MOD_DELETE:
+			Debug(LDAP_DEBUG_ARGS,
+				"ndb_modify_internal: delete %s\n",
+				mod->sm_desc->ad_cname.bv_val, 0, 0);
+			rc = ndb_modify_delete( NA->e, mod, get_permissiveModify(op),
+				text, textbuf, textlen, NULL );
+			assert( rc != LDAP_TYPE_OR_VALUE_EXISTS );
+			if( rc != LDAP_SUCCESS ) {
+				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+					rc, *text, 0);
+			}
+			break;
+
+		case LDAP_MOD_REPLACE:
+			Debug(LDAP_DEBUG_ARGS,
+				"ndb_modify_internal: replace %s\n",
+				mod->sm_desc->ad_cname.bv_val, 0, 0);
+			rc = modify_replace_values( NA->e, mod, get_permissiveModify(op),
+				text, textbuf, textlen );
+			if( rc != LDAP_SUCCESS ) {
+				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+					rc, *text, 0);
+			}
+			break;
+
+		case LDAP_MOD_INCREMENT:
+			Debug(LDAP_DEBUG_ARGS,
+				"ndb_modify_internal: increment %s\n",
+				mod->sm_desc->ad_cname.bv_val, 0, 0);
+			rc = modify_increment_values( NA->e, mod, get_permissiveModify(op),
+				text, textbuf, textlen );
+			if( rc != LDAP_SUCCESS ) {
+				Debug(LDAP_DEBUG_ARGS,
+					"ndb_modify_internal: %d %s\n",
+					rc, *text, 0);
+			}
+			break;
+
+		case SLAP_MOD_SOFTADD:
+			Debug(LDAP_DEBUG_ARGS,
+				"ndb_modify_internal: softadd %s\n",
+				mod->sm_desc->ad_cname.bv_val, 0, 0);
+ 			mod->sm_op = LDAP_MOD_ADD;
+
+			rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
+				text, textbuf, textlen );
+
+ 			mod->sm_op = SLAP_MOD_SOFTADD;
+
+ 			if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ 				rc = LDAP_SUCCESS;
+ 			}
+
+			if( rc != LDAP_SUCCESS ) {
+				Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+					rc, *text, 0);
+			}
+ 			break;
+
+		default:
+			Debug(LDAP_DEBUG_ANY, "ndb_modify_internal: invalid op %d\n",
+				mod->sm_op, 0, 0);
+			*text = "Invalid modify operation";
+			rc = LDAP_OTHER;
+			Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+				rc, *text, 0);
+		}
+
+		if ( rc != LDAP_SUCCESS ) {
+			attrs_free( old );
+			return rc; 
+		}
+
+		/* If objectClass was modified, reset the flags */
+		if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
+			NA->e->e_ocflags = 0;
+			got_oc = 1;
+		}
+	}
+
+	/* check that the entry still obeys the schema */
+	rc = entry_schema_check( op, NA->e, NULL, get_relax(op), 0,
+		text, textbuf, textlen );
+	if ( rc != LDAP_SUCCESS || op->o_noop ) {
+		if ( rc != LDAP_SUCCESS ) {
+			Debug( LDAP_DEBUG_ANY,
+				"entry failed schema check: %s\n",
+				*text, 0, 0 );
+		}
+		attrs_free( old );
+		return rc;
+	}
+
+	/* apply modifications to DB */
+	modai = (NdbAttrInfo **)op->o_tmpalloc( nmods * sizeof(NdbAttrInfo*), op->o_tmpmemctx );
+
+	/* Get the unique list of modified attributes */
+	ldap_pvt_thread_rdwr_rlock( &ni->ni_ai_rwlock );
+	for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+		/* Already took care of objectclass */
+		if ( ml->sml_desc == slap_schema.si_ad_objectClass )
+			continue;
+		for ( i=0; i<nai; i++ ) {
+			if ( ml->sml_desc->ad_type == modai[i]->na_attr )
+				break;
+		}
+		/* This attr was already updated */
+		if ( i < nai )
+			continue;
+		modai[nai] = ndb_ai_find( ni, ml->sml_desc->ad_type );
+		if ( modai[nai]->na_flag & NDB_INFO_INDEX )
+			indexed++;
+		nai++;
+	}
+	ldap_pvt_thread_rdwr_runlock( &ni->ni_ai_rwlock );
+
+	if ( got_oc || indexed ) {
+		rc = ndb_entry_put_info( op->o_bd, NA, 1 );
+		if ( rc ) {
+			attrs_free( old );
+			return rc;
+		}
+	}
+
+	myDict = NA->ndb->getDictionary();
+
+	/* sort modai so that OcInfo's are contiguous */
+	{
+		int j, k;
+		for ( i=0; i<nai; i++ ) {
+			for ( j=i+1; j<nai; j++ ) {
+				if ( modai[i]->na_oi == modai[j]->na_oi )
+					continue;
+				for ( k=j+1; k<nai; k++ ) {
+					if ( modai[i]->na_oi == modai[k]->na_oi ) {
+						atmp = modai[j];
+						modai[j] = modai[k];
+						modai[k] = atmp;
+						break;
+					}
+				}
+				/* there are no more na_oi's that match modai[i] */
+				if ( k == nai ) {
+					i = j;
+				}
+			}
+		}
+	}
+
+	/* One call per table... */
+	for ( i=0; i<nai; i += j ) {
+		atmp = modai[i];
+		for ( j=i+1; j<nai; j++ )
+			if ( atmp->na_oi != modai[j]->na_oi )
+				break;
+		j -= i;
+		myTable = myDict->getTable( atmp->na_oi->no_table.bv_val );
+		if ( !myTable )
+			continue;
+		rc = ndb_oc_attrs( NA->txn, myTable, NA->e, atmp->na_oi, &modai[i], j, old );
+		if ( rc ) break;
+	}
+	attrs_free( old );
+	return rc;
+}
+
+
+int
+ndb_back_modify( Operation *op, SlapReply *rs )
+{
+	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+	Entry		e = {0};
+	int		manageDSAit = get_manageDSAit( op );
+	char textbuf[SLAP_TEXT_BUFLEN];
+	size_t textlen = sizeof textbuf;
+
+	int		num_retries = 0;
+
+	NdbArgs NA;
+	NdbRdns rdns;
+	struct berval matched;
+
+	LDAPControl **preread_ctrl = NULL;
+	LDAPControl **postread_ctrl = NULL;
+	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+	int num_ctrls = 0;
+
+	Debug( LDAP_DEBUG_ARGS, LDAP_XSTRING(ndb_back_modify) ": %s\n",
+		op->o_req_dn.bv_val, 0, 0 );
+
+	ctrls[num_ctrls] = NULL;
+
+	slap_mods_opattrs( op, &op->orm_modlist, 1 );
+
+	e.e_name = op->o_req_dn;
+	e.e_nname = op->o_req_ndn;
+
+	/* Get our NDB handle */
+	rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+	rdns.nr_num = 0;
+	NA.rdns = &rdns;
+	NA.e = &e;
+
+	if( 0 ) {
+retry:	/* transaction retry */
+		NA.txn->close();
+		NA.txn = NULL;
+		if( e.e_attrs ) {
+			attrs_free( e.e_attrs );
+			e.e_attrs = NULL;
+		}
+		Debug(LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_modify) ": retrying...\n", 0, 0, 0);
+		if ( op->o_abandon ) {
+			rs->sr_err = SLAPD_ABANDON;
+			goto return_results;
+		}
+		if ( NA.ocs ) {
+			ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+		}
+		ndb_trans_backoff( ++num_retries );
+	}
+	NA.ocs = NULL;
+
+	/* begin transaction */
+	NA.txn = NA.ndb->startTransaction();
+	rs->sr_text = NULL;
+	if( !NA.txn ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_modify) ": startTransaction failed: %s (%d)\n",
+			NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+
+	/* get entry or ancestor */
+	rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
+	switch( rs->sr_err ) {
+	case 0:
+		break;
+	case LDAP_NO_SUCH_OBJECT:
+		Debug( LDAP_DEBUG_ARGS,
+			"<=- ndb_back_modify: no such object %s\n",
+			op->o_req_dn.bv_val, 0, 0 );
+		rs->sr_matched = matched.bv_val;
+		if (NA.ocs )
+			ndb_check_referral( op, rs, &NA );
+		goto return_results;
+#if 0
+	case DB_LOCK_DEADLOCK:
+	case DB_LOCK_NOTGRANTED:
+		goto retry;
+#endif
+	case LDAP_BUSY:
+		rs->sr_text = "ldap server busy";
+		goto return_results;
+	default:
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+
+	/* acquire and lock entry */
+	rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
+
+	if ( !manageDSAit && is_entry_referral( &e ) ) {
+		/* entry is a referral, don't allow modify */
+		rs->sr_ref = get_entry_referrals( op, &e );
+
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_modify) ": entry is referral\n",
+			0, 0, 0 );
+
+		rs->sr_err = LDAP_REFERRAL;
+		rs->sr_matched = e.e_name.bv_val;
+		rs->sr_flags = REP_REF_MUSTBEFREED;
+		goto return_results;
+	}
+
+	if ( get_assert( op ) &&
+		( test_filter( op, &e, (Filter*)get_assertion( op )) != LDAP_COMPARE_TRUE ))
+	{
+		rs->sr_err = LDAP_ASSERTION_FAILED;
+		goto return_results;
+	}
+
+	if( op->o_preread ) {
+		if( preread_ctrl == NULL ) {
+			preread_ctrl = &ctrls[num_ctrls++];
+			ctrls[num_ctrls] = NULL;
+		}
+		if ( slap_read_controls( op, rs, &e,
+			&slap_pre_read_bv, preread_ctrl ) )
+		{
+			Debug( LDAP_DEBUG_TRACE,
+				"<=- " LDAP_XSTRING(ndb_back_modify) ": pre-read "
+				"failed!\n", 0, 0, 0 );
+			if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+				/* FIXME: is it correct to abort
+				 * operation if control fails? */
+				goto return_results;
+			}
+		}
+	}
+
+	/* Modify the entry */
+	rs->sr_err = ndb_modify_internal( op, &NA, &rs->sr_text, textbuf, textlen );
+
+	if( rs->sr_err != LDAP_SUCCESS ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_modify) ": modify failed (%d)\n",
+			rs->sr_err, 0, 0 );
+#if 0
+		switch( rs->sr_err ) {
+		case DB_LOCK_DEADLOCK:
+		case DB_LOCK_NOTGRANTED:
+			goto retry;
+		}
+#endif
+		goto return_results;
+	}
+
+	if( op->o_postread ) {
+		if( postread_ctrl == NULL ) {
+			postread_ctrl = &ctrls[num_ctrls++];
+			ctrls[num_ctrls] = NULL;
+		}
+		if( slap_read_controls( op, rs, &e,
+			&slap_post_read_bv, postread_ctrl ) )
+		{
+			Debug( LDAP_DEBUG_TRACE,
+				"<=- " LDAP_XSTRING(ndb_back_modify)
+				": post-read failed!\n", 0, 0, 0 );
+			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+				/* FIXME: is it correct to abort
+				 * operation if control fails? */
+				goto return_results;
+			}
+		}
+	}
+
+	if( op->o_noop ) {
+		if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
+			NdbOperation::AbortOnError, 1 )) != 0 ) {
+			rs->sr_text = "txn_abort (no-op) failed";
+		} else {
+			rs->sr_err = LDAP_X_NO_OPERATION;
+		}
+	} else {
+		if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
+			NdbOperation::AbortOnError, 1 )) != 0 ) {
+			rs->sr_text = "txn_commit failed";
+		} else {
+			rs->sr_err = LDAP_SUCCESS;
+		}
+	}
+
+	if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_modify) ": txn_%s failed: %s (%d)\n",
+			op->o_noop ? "abort (no-op)" : "commit",
+			NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+		rs->sr_err = LDAP_OTHER;
+		goto return_results;
+	}
+	NA.txn->close();
+	NA.txn = NULL;
+
+	Debug( LDAP_DEBUG_TRACE,
+		LDAP_XSTRING(ndb_back_modify) ": updated%s id=%08lx dn=\"%s\"\n",
+		op->o_noop ? " (no-op)" : "",
+		e.e_id, op->o_req_dn.bv_val );
+
+	rs->sr_err = LDAP_SUCCESS;
+	rs->sr_text = NULL;
+	if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+	if ( NA.ocs ) {
+		ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+		NA.ocs = NULL;
+	}
+
+	if ( e.e_attrs != NULL ) {
+		attrs_free( e.e_attrs );
+		e.e_attrs = NULL;
+	}
+
+	if( NA.txn != NULL ) {
+		NA.txn->execute( Rollback );
+		NA.txn->close();
+	}
+
+	send_ldap_result( op, rs );
+	slap_graduate_commit_csn( op );
+
+	if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+		slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+		slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+	}
+	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+		slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+		slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+	}
+
+	rs->sr_text = NULL;
+	return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/modrdn.cpp b/servers/slapd/back-ndb/modrdn.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b06331ca5bea9907b4cbd438530d2716d0d5366c
--- /dev/null
+++ b/servers/slapd/back-ndb/modrdn.cpp
@@ -0,0 +1,558 @@
+/* modrdn.cpp - ndb backend modrdn routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-ndb.h"
+
+int
+ndb_back_modrdn( Operation *op, SlapReply *rs )
+{
+	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+	AttributeDescription *children = slap_schema.si_ad_children;
+	AttributeDescription *entry = slap_schema.si_ad_entry;
+	struct berval	new_dn = BER_BVNULL, new_ndn = BER_BVNULL;
+	Entry		e = {0};
+	Entry		e2 = {0};
+	char textbuf[SLAP_TEXT_BUFLEN];
+	size_t textlen = sizeof textbuf;
+
+	struct berval	*np_dn = NULL;			/* newSuperior dn */
+	struct berval	*np_ndn = NULL;			/* newSuperior ndn */
+
+	int		manageDSAit = get_manageDSAit( op );
+	int		num_retries = 0;
+
+	NdbArgs NA, NA2;
+	NdbRdns rdns, rdn2;
+	struct berval matched;
+
+	LDAPControl **preread_ctrl = NULL;
+	LDAPControl **postread_ctrl = NULL;
+	LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+	int num_ctrls = 0;
+
+	int	rc;
+
+	Debug( LDAP_DEBUG_ARGS, "==>" LDAP_XSTRING(ndb_back_modrdn) "(%s,%s,%s)\n",
+		op->o_req_dn.bv_val,op->oq_modrdn.rs_newrdn.bv_val,
+		op->oq_modrdn.rs_newSup ? op->oq_modrdn.rs_newSup->bv_val : "NULL" );
+
+	ctrls[num_ctrls] = NULL;
+
+	slap_mods_opattrs( op, &op->orr_modlist, 1 );
+
+	e.e_name = op->o_req_dn;
+	e.e_nname = op->o_req_ndn;
+
+	/* Get our NDB handle */
+	rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+	rdns.nr_num = 0;
+	NA.rdns = &rdns;
+	NA.e = &e;
+	NA2.ndb = NA.ndb;
+	NA2.e = &e2;
+	NA2.rdns = &rdn2;
+
+	if( 0 ) {
+retry:	/* transaction retry */
+		NA.txn->close();
+		NA.txn = NULL;
+		if ( e.e_attrs ) {
+			attrs_free( e.e_attrs );
+			e.e_attrs = NULL;
+		}
+		Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(ndb_back_modrdn)
+				": retrying...\n", 0, 0, 0 );
+		if ( op->o_abandon ) {
+			rs->sr_err = SLAPD_ABANDON;
+			goto return_results;
+		}
+		if ( NA2.ocs ) {
+			ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx );
+		}
+		if ( NA.ocs ) {
+			ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+		}
+		ndb_trans_backoff( ++num_retries );
+	}
+	NA.ocs = NULL;
+	NA2.ocs = NULL;
+
+	/* begin transaction */
+	NA.txn = NA.ndb->startTransaction();
+	rs->sr_text = NULL;
+	if( !NA.txn ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_modrdn) ": startTransaction failed: %s (%d)\n",
+			NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+	NA2.txn = NA.txn;
+
+	/* get entry */
+	rs->sr_err = ndb_entry_get_info( op, &NA, 1, &matched );
+	switch( rs->sr_err ) {
+	case 0:
+		break;
+	case LDAP_NO_SUCH_OBJECT:
+		Debug( LDAP_DEBUG_ARGS,
+			"<=- ndb_back_modrdn: no such object %s\n",
+			op->o_req_dn.bv_val, 0, 0 );
+		rs->sr_matched = matched.bv_val;
+		if ( NA.ocs )
+			ndb_check_referral( op, rs, &NA );
+		goto return_results;
+#if 0
+	case DB_LOCK_DEADLOCK:
+	case DB_LOCK_NOTGRANTED:
+		goto retry;
+#endif
+	case LDAP_BUSY:
+		rs->sr_text = "ldap server busy";
+		goto return_results;
+	default:
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+
+	/* acquire and lock entry */
+	rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
+	if ( rs->sr_err )
+		goto return_results;
+
+	if ( !manageDSAit && is_entry_glue( &e )) {
+		rs->sr_err = LDAP_NO_SUCH_OBJECT;
+		goto return_results;
+	}
+	
+	if ( get_assert( op ) &&
+		( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
+	{
+		rs->sr_err = LDAP_ASSERTION_FAILED;
+		goto return_results;
+	}
+
+	/* check write on old entry */
+	rs->sr_err = access_allowed( op, &e, entry, NULL, ACL_WRITE, NULL );
+	if ( ! rs->sr_err ) {
+		Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
+			0, 0 );
+		rs->sr_text = "no write access to old entry";
+		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+		goto return_results;
+	}
+
+	/* Can't do it if we have kids */
+	rs->sr_err = ndb_has_children( &NA, &rc );
+	if ( rs->sr_err ) {
+		Debug(LDAP_DEBUG_ARGS,
+			"<=- " LDAP_XSTRING(ndb_back_modrdn)
+			": has_children failed: %s (%d)\n",
+			NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto return_results;
+	}
+	if ( rc == LDAP_COMPARE_TRUE ) {
+		Debug(LDAP_DEBUG_ARGS,
+			"<=- " LDAP_XSTRING(ndb_back_modrdn)
+			": non-leaf %s\n",
+			op->o_req_dn.bv_val, 0, 0);
+		rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+		rs->sr_text = "subtree rename not supported";
+		goto return_results;
+	}
+
+	if (!manageDSAit && is_entry_referral( &e ) ) {
+		/* entry is a referral, don't allow modrdn */
+		rs->sr_ref = get_entry_referrals( op, &e );
+
+		Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn)
+			": entry %s is referral\n", e.e_dn, 0, 0 );
+
+		rs->sr_err = LDAP_REFERRAL,
+		rs->sr_matched = op->o_req_dn.bv_val;
+		rs->sr_flags = REP_REF_MUSTBEFREED;
+		goto return_results;
+	}
+
+	if ( be_issuffix( op->o_bd, &e.e_nname ) ) {
+		/* There can only be one suffix entry */
+		rs->sr_err = LDAP_NAMING_VIOLATION;
+		rs->sr_text = "cannot rename suffix entry";
+		goto return_results;
+	} else {
+		dnParent( &e.e_nname, &e2.e_nname );
+		dnParent( &e.e_name, &e2.e_name );
+	}
+
+	/* check parent for "children" acl */
+	rs->sr_err = access_allowed( op, &e2,
+		children, NULL,
+		op->oq_modrdn.rs_newSup == NULL ?
+			ACL_WRITE : ACL_WDEL,
+		NULL );
+
+	if ( ! rs->sr_err ) {
+		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+		Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
+			0, 0 );
+		rs->sr_text = "no write access to old parent's children";
+		goto return_results;
+	}
+
+	Debug( LDAP_DEBUG_TRACE,
+		LDAP_XSTRING(ndb_back_modrdn) ": wr to children "
+		"of entry %s OK\n", e2.e_name.bv_val, 0, 0 );
+	
+	if ( op->oq_modrdn.rs_newSup != NULL ) {
+		Debug( LDAP_DEBUG_TRACE, 
+			LDAP_XSTRING(ndb_back_modrdn)
+			": new parent \"%s\" requested...\n",
+			op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
+
+		/*  newSuperior == oldParent? */
+		if( dn_match( &e2.e_nname, op->oq_modrdn.rs_nnewSup ) ) {
+			Debug( LDAP_DEBUG_TRACE, "bdb_back_modrdn: "
+				"new parent \"%s\" same as the old parent \"%s\"\n",
+				op->oq_modrdn.rs_newSup->bv_val, e2.e_name.bv_val, 0 );
+			op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
+		}
+	}
+
+	if ( op->oq_modrdn.rs_newSup != NULL ) {
+		if ( op->oq_modrdn.rs_newSup->bv_len ) {
+			rdn2.nr_num = 0;
+			np_dn = op->oq_modrdn.rs_newSup;
+			np_ndn = op->oq_modrdn.rs_nnewSup;
+
+			/* newSuperior == oldParent? - checked above */
+			/* newSuperior == entry being moved?, if so ==> ERROR */
+			if ( dnIsSuffix( np_ndn, &e.e_nname )) {
+				rs->sr_err = LDAP_NO_SUCH_OBJECT;
+				rs->sr_text = "new superior not found";
+				goto return_results;
+			}
+			/* Get Entry with dn=newSuperior. Does newSuperior exist? */
+
+			e2.e_name = *np_dn;
+			e2.e_nname = *np_ndn;
+			rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL );
+			switch( rs->sr_err ) {
+			case 0:
+				break;
+			case LDAP_NO_SUCH_OBJECT:
+				Debug( LDAP_DEBUG_TRACE,
+					LDAP_XSTRING(ndb_back_modrdn)
+					": newSup(ndn=%s) not here!\n",
+					np_ndn->bv_val, 0, 0);
+				rs->sr_text = "new superior not found";
+				goto return_results;
+#if 0
+			case DB_LOCK_DEADLOCK:
+			case DB_LOCK_NOTGRANTED:
+				goto retry;
+#endif
+			case LDAP_BUSY:
+				rs->sr_text = "ldap server busy";
+				goto return_results;
+			default:
+				rs->sr_err = LDAP_OTHER;
+				rs->sr_text = "internal error";
+				goto return_results;
+			}
+			if ( NA2.ocs ) {
+				Attribute a;
+				int i;
+
+				for ( i=0; !BER_BVISNULL( &NA2.ocs[i] ); i++);
+				a.a_numvals = i;
+				a.a_desc = slap_schema.si_ad_objectClass;
+				a.a_vals = NA2.ocs;
+				a.a_nvals = NA2.ocs;
+				a.a_next = NULL;
+				e2.e_attrs = &a;
+
+				if ( is_entry_alias( &e2 )) {
+					/* parent is an alias, don't allow move */
+					Debug( LDAP_DEBUG_TRACE,
+						LDAP_XSTRING(ndb_back_modrdn)
+						": entry is alias\n",
+						0, 0, 0 );
+					rs->sr_text = "new superior is an alias";
+					rs->sr_err = LDAP_ALIAS_PROBLEM;
+					goto return_results;
+				}
+
+				if ( is_entry_referral( &e2 ) ) {
+					/* parent is a referral, don't allow move */
+					Debug( LDAP_DEBUG_TRACE,
+						LDAP_XSTRING(ndb_back_modrdn)
+						": entry is referral\n",
+						0, 0, 0 );
+					rs->sr_text = "new superior is a referral";
+					rs->sr_err = LDAP_OTHER;
+					goto return_results;
+				}
+			}
+		}
+
+		/* check newSuperior for "children" acl */
+		rs->sr_err = access_allowed( op, &e2, children,
+			NULL, ACL_WADD, NULL );
+		if( ! rs->sr_err ) {
+			Debug( LDAP_DEBUG_TRACE,
+				LDAP_XSTRING(ndb_back_modrdn)
+				": no wr to newSup children\n",
+				0, 0, 0 );
+			rs->sr_text = "no write access to new superior's children";
+			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+			goto return_results;
+		}
+
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_modrdn)
+			": wr to new parent OK id=%ld\n",
+			(long) e2.e_id, 0, 0 );
+	}
+
+	/* Build target dn and make sure target entry doesn't exist already. */
+	if (!new_dn.bv_val) {
+		build_new_dn( &new_dn, &e2.e_name, &op->oq_modrdn.rs_newrdn, NULL ); 
+	}
+
+	if (!new_ndn.bv_val) {
+		build_new_dn( &new_ndn, &e2.e_nname, &op->oq_modrdn.rs_nnewrdn, NULL ); 
+	}
+
+	Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn) ": new ndn=%s\n",
+		new_ndn.bv_val, 0, 0 );
+
+	/* Allow rename to same DN */
+	if ( !bvmatch ( &new_ndn, &e.e_nname )) {
+		rdn2.nr_num = 0;
+		e2.e_name = new_dn;
+		e2.e_nname = new_ndn;
+		NA2.ocs = &matched;
+		rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL );
+		NA2.ocs = NULL;
+		switch( rs->sr_err ) {
+#if 0
+		case DB_LOCK_DEADLOCK:
+		case DB_LOCK_NOTGRANTED:
+			goto retry;
+#endif
+		case LDAP_NO_SUCH_OBJECT:
+			break;
+		case 0:
+			rs->sr_err = LDAP_ALREADY_EXISTS;
+			goto return_results;
+		default:
+			rs->sr_err = LDAP_OTHER;
+			rs->sr_text = "internal error";
+			goto return_results;
+		}
+	}
+
+	assert( op->orr_modlist != NULL );
+
+	if( op->o_preread ) {
+		if( preread_ctrl == NULL ) {
+			preread_ctrl = &ctrls[num_ctrls++];
+			ctrls[num_ctrls] = NULL;
+		}
+		if( slap_read_controls( op, rs, &e,
+			&slap_pre_read_bv, preread_ctrl ) )
+		{
+			Debug( LDAP_DEBUG_TRACE,        
+				"<=- " LDAP_XSTRING(ndb_back_modrdn)
+				": pre-read failed!\n", 0, 0, 0 );
+			if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+				/* FIXME: is it correct to abort
+				 * operation if control fails? */
+				goto return_results;
+			}
+		}                   
+	}
+
+	/* delete old DN */
+	rs->sr_err = ndb_entry_del_info( op->o_bd, &NA );
+	if ( rs->sr_err != 0 ) {
+		Debug(LDAP_DEBUG_TRACE,
+			"<=- " LDAP_XSTRING(ndb_back_modrdn)
+			": dn2id del failed: %s (%d)\n",
+			NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+#if 0
+		switch( rs->sr_err ) {
+		case DB_LOCK_DEADLOCK:
+		case DB_LOCK_NOTGRANTED:
+			goto retry;
+		}
+#endif
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "DN index delete fail";
+		goto return_results;
+	}
+
+	/* copy entry fields */
+	e2.e_attrs = e.e_attrs;
+	e2.e_id = e.e_id;
+
+	/* add new DN */
+	rs->sr_err = ndb_entry_put_info( op->o_bd, &NA2, 0 );
+	if ( rs->sr_err != 0 ) {
+		Debug(LDAP_DEBUG_TRACE,
+			"<=- " LDAP_XSTRING(ndb_back_modrdn)
+			": dn2id add failed: %s (%d)\n",
+			NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+#if 0
+		switch( rs->sr_err ) {
+		case DB_LOCK_DEADLOCK:
+		case DB_LOCK_NOTGRANTED:
+			goto retry;
+		}
+#endif
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "DN index add failed";
+		goto return_results;
+	}
+
+	/* modify entry */
+	rs->sr_err = ndb_modify_internal( op, &NA2,
+		&rs->sr_text, textbuf, textlen );
+	if( rs->sr_err != LDAP_SUCCESS ) {
+		Debug(LDAP_DEBUG_TRACE,
+			"<=- " LDAP_XSTRING(ndb_back_modrdn)
+			": modify failed: %s (%d)\n",
+			NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+#if 0
+		switch( rs->sr_err ) {
+		case DB_LOCK_DEADLOCK:
+		case DB_LOCK_NOTGRANTED:
+			goto retry;
+		}
+#endif
+		goto return_results;
+	}
+
+	e.e_attrs = e2.e_attrs;
+
+	if( op->o_postread ) {
+		if( postread_ctrl == NULL ) {
+			postread_ctrl = &ctrls[num_ctrls++];
+			ctrls[num_ctrls] = NULL;
+		}
+		if( slap_read_controls( op, rs, &e2,
+			&slap_post_read_bv, postread_ctrl ) )
+		{
+			Debug( LDAP_DEBUG_TRACE,        
+				"<=- " LDAP_XSTRING(ndb_back_modrdn)
+				": post-read failed!\n", 0, 0, 0 );
+			if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+				/* FIXME: is it correct to abort
+				 * operation if control fails? */
+				goto return_results;
+			}
+		}                   
+	}
+
+	if( op->o_noop ) {
+		if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
+			NdbOperation::AbortOnError, 1 )) != 0 ) {
+			rs->sr_text = "txn_abort (no-op) failed";
+		} else {
+			rs->sr_err = LDAP_X_NO_OPERATION;
+		}
+	} else {
+		if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
+			NdbOperation::AbortOnError, 1 )) != 0 ) {
+			rs->sr_text = "txn_commit failed";
+		} else {
+			rs->sr_err = LDAP_SUCCESS;
+		}
+	}
+ 
+	if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_modrdn) ": txn_%s failed: %s (%d)\n",
+			op->o_noop ? "abort (no-op)" : "commit",
+			NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+		rs->sr_err = LDAP_OTHER;
+		goto return_results;
+	}
+	NA.txn->close();
+	NA.txn = NULL;
+
+	Debug(LDAP_DEBUG_TRACE,
+		LDAP_XSTRING(ndb_back_modrdn)
+		": rdn modified%s id=%08lx dn=\"%s\"\n",
+		op->o_noop ? " (no-op)" : "",
+		e.e_id, op->o_req_dn.bv_val );
+
+	rs->sr_err = LDAP_SUCCESS;
+	rs->sr_text = NULL;
+	if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+	if ( NA2.ocs ) {
+		ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx );
+		NA2.ocs = NULL;
+	}
+
+	if ( NA.ocs ) {
+		ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+		NA.ocs = NULL;
+	}
+
+	if ( e.e_attrs ) {
+		attrs_free( e.e_attrs );
+		e.e_attrs = NULL;
+	}
+
+	if( NA.txn != NULL ) {
+		NA.txn->execute( Rollback );
+		NA.txn->close();
+	}
+
+	send_ldap_result( op, rs );
+	slap_graduate_commit_csn( op );
+
+	if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
+	if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
+
+	if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+		slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+		slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+	}
+	if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+		slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+		slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+	}
+
+	rs->sr_text = NULL;
+	return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/ndbio.cpp b/servers/slapd/back-ndb/ndbio.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..195a0306751a69ac615e985d1d28a855432948c2
--- /dev/null
+++ b/servers/slapd/back-ndb/ndbio.cpp
@@ -0,0 +1,1677 @@
+/* ndbio.cpp - get/set/del data for NDB */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+#include <lutil.h>
+
+#include "back-ndb.h"
+
+/* For reference only */
+typedef struct MedVar {
+	Int16 len;	/* length is always little-endian */
+	char buf[1024];
+} MedVar;
+
+extern "C" {
+	static int ndb_name_cmp( const void *v1, const void *v2 );
+	static int ndb_oc_dup_err( void *v1, void *v2 );
+};
+
+static int
+ndb_name_cmp( const void *v1, const void *v2 )
+{
+	NdbOcInfo *oc1 = (NdbOcInfo *)v1, *oc2 = (NdbOcInfo *)v2;
+	return ber_bvstrcasecmp( &oc1->no_name, &oc2->no_name );
+}
+
+static int
+ndb_oc_dup_err( void *v1, void *v2 )
+{
+	NdbOcInfo *oc = (NdbOcInfo *)v2;
+
+	oc->no_oc = (ObjectClass *)v1;
+	return -1;
+}
+
+/* Find an existing NdbAttrInfo */
+extern "C" NdbAttrInfo *
+ndb_ai_find( struct ndb_info *ni, AttributeType *at )
+{
+	NdbAttrInfo atmp;
+	atmp.na_name = at->sat_cname;
+
+	return (NdbAttrInfo *)avl_find( ni->ni_ai_tree, &atmp, ndb_name_cmp );
+}
+
+/* Find or create an NdbAttrInfo */
+extern "C" NdbAttrInfo *
+ndb_ai_get( struct ndb_info *ni, struct berval *aname )
+{
+	NdbAttrInfo atmp, *ai;
+	atmp.na_name = *aname;
+
+	ai = (NdbAttrInfo *)avl_find( ni->ni_ai_tree, &atmp, ndb_name_cmp );
+	if ( !ai ) {
+		const char *text;
+		AttributeDescription *ad = NULL;
+
+		if ( slap_bv2ad( aname, &ad, &text ))
+			return NULL;
+
+		ai = (NdbAttrInfo *)ch_malloc( sizeof( NdbAttrInfo ));
+		ai->na_desc = ad;
+		ai->na_attr = ai->na_desc->ad_type;
+		ai->na_name = ai->na_attr->sat_cname;
+		ai->na_oi = NULL;
+		ai->na_flag = 0;
+		ai->na_ixcol = 0;
+		ai->na_len = ai->na_attr->sat_atype.at_syntax_len;
+		/* Reasonable default */
+		if ( !ai->na_len ) {
+			if ( ai->na_attr->sat_syntax == slap_schema.si_syn_distinguishedName )
+				ai->na_len = 1024;
+			else
+				ai->na_len = 128;
+		}
+		/* Arbitrary limit */
+		if ( ai->na_len > 1024 )
+			ai->na_len = 1024;
+		avl_insert( &ni->ni_ai_tree, ai, ndb_name_cmp, avl_dup_error );
+	}
+	return ai;
+}
+
+static int
+ndb_ai_check( struct ndb_info *ni, NdbOcInfo *oci, AttributeType **attrs, char **ptr, int *col,
+	int create )
+{
+	NdbAttrInfo *ai;
+	int i;
+
+	for ( i=0; attrs[i]; i++ ) {
+		if ( attrs[i] == slap_schema.si_ad_objectClass->ad_type )
+			continue;
+		/* skip attrs that are in a superior */
+		if ( oci->no_oc && oci->no_oc->soc_sups ) {
+			int j, k, found=0;
+			ObjectClass *oc;
+			for ( j=0; oci->no_oc->soc_sups[j]; j++ ) {
+				oc = oci->no_oc->soc_sups[j];
+				if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT )
+					continue;
+				if ( oc->soc_required ) {
+					for ( k=0; oc->soc_required[k]; k++ ) {
+						if ( attrs[i] == oc->soc_required[k] ) {
+							found = 1;
+							break;
+						}
+					}
+					if ( found ) break;
+				}
+				if ( oc->soc_allowed ) {
+					for ( k=0; oc->soc_allowed[k]; k++ ) {
+						if ( attrs[i] == oc->soc_allowed[k] ) {
+							found = 1;
+							break;
+						}
+					}
+					if ( found ) break;
+				}
+			}
+			if ( found )
+				continue;
+		}
+
+		ai = ndb_ai_get( ni, &attrs[i]->sat_cname );
+		if ( !ai ) {
+			/* can never happen */
+			return LDAP_OTHER;
+		}
+
+		/* An attrset may have already been connected */
+		if (( oci->no_flag & NDB_INFO_ATSET ) && ai->na_oi == oci )
+			continue;
+
+		/* An indexed attr is defined before its OC is */
+		if ( !ai->na_oi ) {
+			ai->na_oi = oci;
+			ai->na_column = (*col)++;
+		}
+
+		oci->no_attrs[oci->no_nattrs++] = ai;
+
+		/* An attrset attr may already be defined */
+		if ( ai->na_oi != oci ) {
+			int j;
+			for ( j=0; j<oci->no_nsets; j++ )
+				if ( oci->no_sets[j] == ai->na_oi ) break;
+			if ( j >= oci->no_nsets ) {
+				/* FIXME: data loss if more sets are in use */
+				if ( oci->no_nsets < NDB_MAX_OCSETS ) {
+					oci->no_sets[oci->no_nsets++] = ai->na_oi;
+				}
+			}
+			continue;
+		}
+
+		if ( create ) {
+			if ( ai->na_flag & NDB_INFO_ATBLOB ) {
+				*ptr += sprintf( *ptr, ", `%s` BLOB", ai->na_attr->sat_cname.bv_val );
+			} else {
+				*ptr += sprintf( *ptr, ", `%s` VARCHAR(%d)", ai->na_attr->sat_cname.bv_val,
+					ai->na_len );
+			}
+		}
+	}
+	return 0;
+}
+
+static int
+ndb_oc_create( struct ndb_info *ni, NdbOcInfo *oci, int create )
+{
+	char buf[4096], *ptr;
+	int i, rc = 0, col;
+
+	if ( create ) {
+		ptr = buf + sprintf( buf,
+			"CREATE TABLE `%s` (eid bigint unsigned NOT NULL, vid int unsigned NOT NULL",
+			oci->no_table.bv_val );
+	}
+
+	col = 0;
+	if ( oci->no_oc->soc_required ) {
+		for ( i=0; oci->no_oc->soc_required[i]; i++ );
+		col += i;
+	}
+	if ( oci->no_oc->soc_allowed ) {
+		for ( i=0; oci->no_oc->soc_allowed[i]; i++ );
+		col += i;
+	}
+	/* assume all are present */
+	oci->no_attrs = (struct ndb_attrinfo **)ch_malloc( col * sizeof(struct ndb_attrinfo *));
+
+	col = 2;
+	ldap_pvt_thread_rdwr_wlock( &ni->ni_ai_rwlock );
+	if ( oci->no_oc->soc_required ) {
+		rc = ndb_ai_check( ni, oci, oci->no_oc->soc_required, &ptr, &col, create );
+	}
+	if ( !rc && oci->no_oc->soc_allowed ) {
+		rc = ndb_ai_check( ni, oci, oci->no_oc->soc_allowed, &ptr, &col, create );
+	}
+	ldap_pvt_thread_rdwr_wunlock( &ni->ni_ai_rwlock );
+
+	/* shrink down to just the needed size */
+	oci->no_attrs = (struct ndb_attrinfo **)ch_realloc( oci->no_attrs,
+		oci->no_nattrs * sizeof(struct ndb_attrinfo *));
+
+	if ( create ) {
+		ptr = lutil_strcopy( ptr, ", PRIMARY KEY(eid, vid) ) ENGINE=ndb PARTITION BY KEY(eid)" );
+		rc = mysql_real_query( &ni->ni_sql, buf, ptr - buf );
+		if ( rc ) {
+			Debug( LDAP_DEBUG_ANY,
+				"ndb_oc_create: CREATE TABLE %s failed, %s (%d)\n",
+				oci->no_table.bv_val, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+		}
+	}
+	return rc;
+}
+
+/* Read table definitions from the DB and populate ObjectClassInfo */
+extern "C" int
+ndb_oc_read( struct ndb_info *ni, const NdbDictionary::Dictionary *myDict )
+{
+	const NdbDictionary::Table *myTable;
+	const NdbDictionary::Column *myCol;
+	NdbOcInfo *oci, octmp;
+	NdbAttrInfo *ai;
+	ObjectClass *oc;
+	NdbDictionary::Dictionary::List myList;
+	struct berval bv;
+	int i, j, rc, col;
+
+	rc = myDict->listObjects( myList, NdbDictionary::Object::UserTable );
+	/* Populate our objectClass structures */
+	for ( i=0; i<myList.count; i++ ) {
+		/* Ignore other DBs */
+		if ( strcmp( myList.elements[i].database, ni->ni_dbname ))
+			continue;
+		/* Ignore internal tables */
+		if ( !strncmp( myList.elements[i].name, "OL_", 3 ))
+			continue;
+		ber_str2bv( myList.elements[i].name, 0, 0, &octmp.no_name );
+		oci = (NdbOcInfo *)avl_find( ni->ni_oc_tree, &octmp, ndb_name_cmp );
+		if ( oci )
+			continue;
+
+		oc = oc_bvfind( &octmp.no_name );
+		if ( !oc ) {
+			/* undefined - shouldn't happen */
+			continue;
+		}
+		myTable = myDict->getTable( myList.elements[i].name );
+		oci = (NdbOcInfo *)ch_malloc( sizeof( NdbOcInfo )+oc->soc_cname.bv_len+1 );
+		oci->no_table.bv_val = (char *)(oci+1);
+		strcpy( oci->no_table.bv_val, oc->soc_cname.bv_val );
+		oci->no_table.bv_len = oc->soc_cname.bv_len;
+		oci->no_name = oci->no_table;
+		oci->no_oc = oc;
+		oci->no_flag = 0;
+		oci->no_nsets = 0;
+		oci->no_nattrs = 0;
+		col = 0;
+		/* Make space for all attrs, even tho sups will be dropped */
+		if ( oci->no_oc->soc_required ) {
+			for ( j=0; oci->no_oc->soc_required[j]; j++ );
+			col = j;
+		}
+		if ( oci->no_oc->soc_allowed ) {
+			for ( j=0; oci->no_oc->soc_allowed[j]; j++ );
+			col += j;
+		}
+		oci->no_attrs = (struct ndb_attrinfo **)ch_malloc( col * sizeof(struct ndb_attrinfo *));
+		avl_insert( &ni->ni_oc_tree, oci, ndb_name_cmp, avl_dup_error );
+
+		col = myTable->getNoOfColumns();
+		/* Skip 0 and 1, eid and vid */
+		for ( j = 2; j<col; j++ ) {
+			myCol = myTable->getColumn( j );
+			ber_str2bv( myCol->getName(), 0, 0, &bv );
+			ai = ndb_ai_get( ni, &bv );
+			/* shouldn't happen */
+			if ( !ai )
+				continue;
+			ai->na_oi = oci;
+			ai->na_column = j;
+			ai->na_len = myCol->getLength();
+			if ( myCol->getType() == NdbDictionary::Column::Blob )
+				ai->na_flag |= NDB_INFO_ATBLOB;
+		}
+	}
+	/* Link to any attrsets */
+	for ( i=0; i<myList.count; i++ ) {
+		/* Ignore other DBs */
+		if ( strcmp( myList.elements[i].database, ni->ni_dbname ))
+			continue;
+		/* Ignore internal tables */
+		if ( !strncmp( myList.elements[i].name, "OL_", 3 ))
+			continue;
+		ber_str2bv( myList.elements[i].name, 0, 0, &octmp.no_name );
+		oci = (NdbOcInfo *)avl_find( ni->ni_oc_tree, &octmp, ndb_name_cmp );
+		/* shouldn't happen */
+		if ( !oci )
+			continue;
+		col = 2;
+		if ( oci->no_oc->soc_required ) {
+			rc = ndb_ai_check( ni, oci, oci->no_oc->soc_required, NULL, &col, 0 );
+		}
+		if ( oci->no_oc->soc_allowed ) {
+			rc = ndb_ai_check( ni, oci, oci->no_oc->soc_allowed, NULL, &col, 0 );
+		}
+		/* shrink down to just the needed size */
+		oci->no_attrs = (struct ndb_attrinfo **)ch_realloc( oci->no_attrs,
+			oci->no_nattrs * sizeof(struct ndb_attrinfo *));
+	}
+	return 0;
+}
+
+static int
+ndb_oc_list( struct ndb_info *ni, const NdbDictionary::Dictionary *myDict,
+	struct berval *oname, int implied, NdbOcs *out )
+{
+	const NdbDictionary::Table *myTable;
+	NdbOcInfo *oci, octmp;
+	ObjectClass *oc;
+	int i, rc;
+
+	/* shortcut top */
+	if ( ber_bvstrcasecmp( oname, &slap_schema.si_oc_top->soc_cname )) {
+		octmp.no_name = *oname;
+		oci = (NdbOcInfo *)avl_find( ni->ni_oc_tree, &octmp, ndb_name_cmp );
+		if ( oci ) {
+			oc = oci->no_oc;
+		} else {
+			oc = oc_bvfind( oname );
+			if ( !oc ) {
+				/* undefined - shouldn't happen */
+				return LDAP_INVALID_SYNTAX;
+			}
+		}
+		if ( oc->soc_sups ) {
+			int i;
+
+			for ( i=0; oc->soc_sups[i]; i++ ) {
+				rc = ndb_oc_list( ni, myDict, &oc->soc_sups[i]->soc_cname, 1, out );
+				if ( rc ) return rc;
+			}
+		}
+	} else {
+		oc = slap_schema.si_oc_top;
+	}
+	/* Only insert once */
+	for ( i=0; i<out->no_ntext; i++ )
+		if ( out->no_text[i].bv_val == oc->soc_cname.bv_val )
+			break;
+	if ( i == out->no_ntext ) {
+		for ( i=0; i<out->no_nitext; i++ )
+			if ( out->no_itext[i].bv_val == oc->soc_cname.bv_val )
+				break;
+		if ( i == out->no_nitext ) {
+			if ( implied )
+				out->no_itext[out->no_nitext++] = oc->soc_cname;
+			else
+				out->no_text[out->no_ntext++] = oc->soc_cname;
+		}
+	}
+
+	/* ignore top, etc... */
+	if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT )
+		return 0;
+
+	if ( !oci ) {
+		ldap_pvt_thread_rdwr_runlock( &ni->ni_oc_rwlock );
+		oci = (NdbOcInfo *)ch_malloc( sizeof( NdbOcInfo )+oc->soc_cname.bv_len+1 );
+		oci->no_table.bv_val = (char *)(oci+1);
+		strcpy( oci->no_table.bv_val, oc->soc_cname.bv_val );
+		oci->no_table.bv_len = oc->soc_cname.bv_len;
+		oci->no_name = oci->no_table;
+		oci->no_oc = oc;
+		oci->no_flag = 0;
+		oci->no_nsets = 0;
+		oci->no_nattrs = 0;
+		ldap_pvt_thread_rdwr_wlock( &ni->ni_oc_rwlock );
+		if ( avl_insert( &ni->ni_oc_tree, oci, ndb_name_cmp, ndb_oc_dup_err )) {
+			octmp.no_oc = oci->no_oc;
+			ch_free( oci );
+			oci = (NdbOcInfo *)octmp.no_oc;
+		}
+		/* see if the oc table already exists in the DB */
+		myTable = myDict->getTable( oci->no_table.bv_val );
+		rc = ndb_oc_create( ni, oci, myTable == NULL );
+		ldap_pvt_thread_rdwr_wunlock( &ni->ni_oc_rwlock );
+		ldap_pvt_thread_rdwr_rlock( &ni->ni_oc_rwlock );
+		if ( rc ) return rc;
+	}
+	/* Only insert once */
+	for ( i=0; i<out->no_ninfo; i++ )
+		if ( out->no_info[i] == oci )
+			break;
+	if ( i == out->no_ninfo )
+		out->no_info[out->no_ninfo++] = oci;
+	return 0;
+}
+
+extern "C" int
+ndb_aset_get( struct ndb_info *ni, struct berval *sname, struct berval *attrs, NdbOcInfo **ret )
+{
+	NdbOcInfo *oci, octmp;
+	int i, rc;
+
+	octmp.no_name = *sname;
+	oci = (NdbOcInfo *)avl_find( ni->ni_oc_tree, &octmp, ndb_name_cmp );
+	if ( oci )
+		return LDAP_ALREADY_EXISTS;
+
+	for ( i=0; !BER_BVISNULL( &attrs[i] ); i++ ) {
+		if ( !at_bvfind( &attrs[i] ))
+			return LDAP_NO_SUCH_ATTRIBUTE;
+	}
+	i++;
+
+	oci = (NdbOcInfo *)ch_calloc( 1, sizeof( NdbOcInfo ) + sizeof( ObjectClass ) +
+		i*sizeof(AttributeType *) + sname->bv_len+1 );
+	oci->no_oc = (ObjectClass *)(oci+1);
+	oci->no_oc->soc_required = (AttributeType **)(oci->no_oc+1);
+	oci->no_table.bv_val = (char *)(oci->no_oc->soc_required+i);
+
+	for ( i=0; !BER_BVISNULL( &attrs[i] ); i++ )
+		oci->no_oc->soc_required[i] = at_bvfind( &attrs[i] );
+
+	strcpy( oci->no_table.bv_val, sname->bv_val );
+	oci->no_table.bv_len = sname->bv_len;
+	oci->no_name = oci->no_table;
+	oci->no_oc->soc_cname = oci->no_name;
+	oci->no_flag = NDB_INFO_ATSET;
+
+	if ( !ber_bvcmp( sname, &slap_schema.si_oc_extensibleObject->soc_cname ))
+		oci->no_oc->soc_kind = slap_schema.si_oc_extensibleObject->soc_kind;
+
+	rc = ndb_oc_create( ni, oci, 0 );
+	if ( !rc )
+		rc = avl_insert( &ni->ni_oc_tree, oci, ndb_name_cmp, avl_dup_error );
+	if ( rc ) {
+		ch_free( oci );
+	} else {
+		*ret = oci;
+	}
+	return rc;
+}
+
+extern "C" int
+ndb_aset_create( struct ndb_info *ni, NdbOcInfo *oci )
+{
+	char buf[4096], *ptr;
+	NdbAttrInfo *ai;
+	int i;
+
+	ptr = buf + sprintf( buf,
+		"CREATE TABLE IF NOT EXISTS `%s` (eid bigint unsigned NOT NULL, vid int unsigned NOT NULL",
+		oci->no_table.bv_val );
+
+	for ( i=0; i<oci->no_nattrs; i++ ) {
+		if ( oci->no_attrs[i]->na_oi != oci )
+			continue;
+		ai = oci->no_attrs[i];
+		ptr += sprintf( ptr, ", `%s` VARCHAR(%d)", ai->na_attr->sat_cname.bv_val,
+			ai->na_len );
+		if ( ai->na_flag & NDB_INFO_INDEX ) {
+			ptr += sprintf( ptr, ", INDEX (`%s`)", ai->na_attr->sat_cname.bv_val );
+		}
+	}
+	ptr = lutil_strcopy( ptr, ", PRIMARY KEY(eid, vid) ) ENGINE=ndb PARTITION BY KEY(eid)" );
+	i = mysql_real_query( &ni->ni_sql, buf, ptr - buf );
+	if ( i ) {
+		Debug( LDAP_DEBUG_ANY,
+			"ndb_aset_create: CREATE TABLE %s failed, %s (%d)\n",
+			oci->no_table.bv_val, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+	}
+	return i;
+}
+
+static int
+ndb_oc_check( BackendDB *be, Ndb *ndb,
+	struct berval *ocsin, NdbOcs *out )
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+	const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
+
+	int i, rc = 0;
+
+	out->no_ninfo = 0;
+	out->no_ntext = 0;
+	out->no_nitext = 0;
+
+	/* Find all objectclasses and their superiors. List
+	 * the superiors first.
+	 */
+
+	ldap_pvt_thread_rdwr_rlock( &ni->ni_oc_rwlock );
+	for ( i=0; !BER_BVISNULL( &ocsin[i] ); i++ ) {
+		rc = ndb_oc_list( ni, myDict, &ocsin[i], 0, out );
+		if ( rc ) break;
+	}
+	ldap_pvt_thread_rdwr_runlock( &ni->ni_oc_rwlock );
+	return rc;
+}
+
+#define	V_INS	1
+#define	V_DEL	2
+#define	V_REP	3
+
+static int ndb_flush_blobs;
+
+/* set all the unique attrs of this objectclass into the table
+ */
+extern "C" int
+ndb_oc_attrs(
+	NdbTransaction *txn,
+	const NdbDictionary::Table *myTable,
+	Entry *e,
+	NdbOcInfo *no,
+	NdbAttrInfo **attrs,
+	int nattrs,
+	Attribute *old
+)
+{
+	char buf[65538], *ptr;
+	Attribute **an, **ao, *a;
+	NdbOperation *myop;
+	int i, j, max = 0;
+	int changed, rc;
+	Uint64 eid = e->e_id;
+
+	if ( !nattrs )
+		return 0;
+
+	an = (Attribute **)ch_malloc( 2 * nattrs * sizeof(Attribute *));
+	ao = an + nattrs;
+
+	/* Turn lists of attrs into arrays for easier access */
+	for ( i=0; i<nattrs; i++ ) {
+		if ( attrs[i]->na_oi != no ) {
+			an[i] = NULL;
+			ao[i] = NULL;
+			continue;
+		}
+		for ( a=e->e_attrs; a; a=a->a_next ) {
+			if ( a->a_desc == slap_schema.si_ad_objectClass )
+				continue;
+			if ( a->a_desc->ad_type == attrs[i]->na_attr ) {
+				/* Don't process same attr twice */
+				if ( a->a_flags & SLAP_ATTR_IXADD )
+					a = NULL;
+				else
+					a->a_flags |= SLAP_ATTR_IXADD;
+				break;
+			}
+		}
+		an[i] = a;
+		if ( a && a->a_numvals > max )
+			max = a->a_numvals;
+		for ( a=old; a; a=a->a_next ) {
+			if ( a->a_desc == slap_schema.si_ad_objectClass )
+				continue;
+			if ( a->a_desc->ad_type == attrs[i]->na_attr )
+				break;
+		}
+		ao[i] = a;
+		if ( a && a->a_numvals > max )
+			max = a->a_numvals;
+	}
+
+	for ( i=0; i<max; i++ ) {
+		myop = NULL;
+		for ( j=0; j<nattrs; j++ ) {
+			if ( !an[j] && !ao[j] )
+				continue;
+			changed = 0;
+			if ( an[j] && an[j]->a_numvals > i ) {
+				/* both old and new are present, compare for changes */
+				if ( ao[j] && ao[j]->a_numvals > i ) {
+					if ( ber_bvcmp( &ao[j]->a_nvals[i], &an[j]->a_nvals[i] ))
+						changed = V_REP;
+				} else {
+					changed = V_INS;
+				}
+			} else {
+				if ( ao[j] && ao[j]->a_numvals > i )
+					changed = V_DEL;
+			}
+			if ( changed ) {
+				if ( !myop ) {
+					rc = LDAP_OTHER;
+					myop = txn->getNdbOperation( myTable );
+					if ( !myop ) {
+						goto done;
+					}
+					if ( old ) {
+						if ( myop->writeTuple()) {
+							goto done;
+						}
+					} else {
+						if ( myop->insertTuple()) {
+							goto done;
+						}
+					}
+					if ( myop->equal( EID_COLUMN, eid )) {
+						goto done;
+					}
+					if ( myop->equal( VID_COLUMN, i )) {
+						goto done;
+					}
+				}
+				if ( attrs[j]->na_flag & NDB_INFO_ATBLOB ) {
+					NdbBlob *myBlob = myop->getBlobHandle( attrs[j]->na_column );
+					rc = LDAP_OTHER;
+					if ( !myBlob ) {
+						Debug( LDAP_DEBUG_TRACE, "ndb_oc_attrs: getBlobHandle failed %s (%d)\n",
+							myop->getNdbError().message, myop->getNdbError().code, 0 );
+						goto done;
+					}
+					if ( slapMode & SLAP_TOOL_MODE )
+						ndb_flush_blobs = 1;
+					if ( changed & V_INS ) {
+						if ( myBlob->setValue( an[j]->a_vals[i].bv_val, an[j]->a_vals[i].bv_len )) {
+							Debug( LDAP_DEBUG_TRACE, "ndb_oc_attrs: blob->setValue failed %s (%d)\n",
+								myBlob->getNdbError().message, myBlob->getNdbError().code, 0 );
+							goto done;
+						}
+					} else {
+						if ( myBlob->setValue( NULL, 0 )) {
+							Debug( LDAP_DEBUG_TRACE, "ndb_oc_attrs: blob->setValue failed %s (%d)\n",
+								myBlob->getNdbError().message, myBlob->getNdbError().code, 0 );
+							goto done;
+						}
+					}
+				} else {
+					if ( changed & V_INS ) {
+						if ( an[j]->a_vals[i].bv_len > attrs[j]->na_len ) {
+							Debug( LDAP_DEBUG_ANY, "ndb_oc_attrs: attribute %s too long for column\n",
+								attrs[j]->na_name.bv_val, 0, 0 );
+							rc = LDAP_CONSTRAINT_VIOLATION;
+							goto done;
+						}
+						ptr = buf;
+						*ptr++ = an[j]->a_vals[i].bv_len & 0xff;
+						if ( attrs[j]->na_len > 255 ) {
+							/* MedVar */
+							*ptr++ = an[j]->a_vals[i].bv_len >> 8;
+						}
+						memcpy( ptr, an[j]->a_vals[i].bv_val, an[j]->a_vals[i].bv_len );
+						ptr = buf;
+					} else {
+						ptr = NULL;
+					}
+					if ( myop->setValue( attrs[j]->na_column, ptr )) {
+						rc = LDAP_OTHER;
+						goto done;
+					}
+				}
+			}
+		}
+	}
+	rc = LDAP_SUCCESS;
+done:
+	ch_free( an );
+	if ( rc ) {
+		Debug( LDAP_DEBUG_TRACE, "ndb_oc_attrs: failed %s (%d)\n",
+			myop->getNdbError().message, myop->getNdbError().code, 0 );
+	}
+	return rc;
+}
+
+static int
+ndb_oc_put(
+	const NdbDictionary::Dictionary *myDict,
+	NdbTransaction *txn, NdbOcInfo *no, Entry *e )
+{
+	const NdbDictionary::Table *myTable;
+	int i, rc;
+
+	for ( i=0; i<no->no_nsets; i++ ) {
+		rc = ndb_oc_put( myDict, txn, no->no_sets[i], e );
+		if ( rc )
+			return rc;
+	}
+
+	myTable = myDict->getTable( no->no_table.bv_val );
+	if ( !myTable )
+		return LDAP_OTHER;
+
+	return ndb_oc_attrs( txn, myTable, e, no, no->no_attrs, no->no_nattrs, NULL );
+}
+
+/* This is now only used for Adds. Modifies call ndb_oc_attrs directly. */
+extern "C" int
+ndb_entry_put_data(
+	BackendDB *be,
+	NdbArgs *NA
+)
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+	Attribute *aoc;
+	const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+	NdbOcs myOcs;
+	int i, rc;
+
+	/* Get the entry's objectClass attribute */
+	aoc = attr_find( NA->e->e_attrs, slap_schema.si_ad_objectClass );
+	if ( !aoc )
+		return LDAP_OTHER;
+
+	ndb_oc_check( be, NA->ndb, aoc->a_nvals, &myOcs );
+	myOcs.no_info[myOcs.no_ninfo++] = ni->ni_opattrs;
+
+	/* Walk thru objectclasses, find all the attributes belonging to a class */
+	for ( i=0; i<myOcs.no_ninfo; i++ ) {
+		rc = ndb_oc_put( myDict, NA->txn, myOcs.no_info[i], NA->e );
+		if ( rc ) return rc;
+	}
+
+	/* slapadd tries to batch multiple entries per txn, but entry data is
+	 * transient and blob data is required to remain valid for the whole txn.
+	 * So we need to flush blobs before their source data disappears.
+	 */
+	if (( slapMode & SLAP_TOOL_MODE ) && ndb_flush_blobs )
+		NA->txn->execute( NdbTransaction::NoCommit );
+
+	return 0;
+}
+
+static void
+ndb_oc_get( Operation *op, NdbOcInfo *no, int *j, int *nocs, NdbOcInfo ***oclist )
+{
+	int i;
+	NdbOcInfo  **ol2;
+
+	for ( i=0; i<no->no_nsets; i++ ) {
+		ndb_oc_get( op, no->no_sets[i], j, nocs, oclist );
+	}
+
+	/* Don't insert twice */
+	ol2 = *oclist;
+	for ( i=0; i<*j; i++ )
+		if ( ol2[i] == no )
+			return;
+
+	if ( *j >= *nocs ) {
+		*nocs *= 2;
+		ol2 = (NdbOcInfo **)op->o_tmprealloc( *oclist, *nocs * sizeof(NdbOcInfo *), op->o_tmpmemctx );
+		*oclist = ol2;
+	}
+	ol2 = *oclist;
+	ol2[(*j)++] = no;
+}
+
+/* Retrieve attribute data for given entry. The entry's DN and eid should
+ * already be populated.
+ */
+extern "C" int
+ndb_entry_get_data(
+	Operation *op,
+	NdbArgs *NA,
+	int update
+)
+{
+	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+	const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+	const NdbDictionary::Table *myTable;
+	NdbIndexScanOperation **myop = NULL;
+	Uint64 eid;
+
+	Attribute *a;
+	NdbOcs myOcs;
+	NdbOcInfo *oci, **oclist = NULL;
+	char abuf[65536], *ptr, **attrs = NULL;
+	struct berval bv[2];
+	int *ocx = NULL;
+
+	/* FIXME: abuf should be dynamically allocated */
+
+	int i, j, k, nocs, nattrs, rc = LDAP_OTHER;
+
+	eid = NA->e->e_id;
+
+	ndb_oc_check( op->o_bd, NA->ndb, NA->ocs, &myOcs );
+	myOcs.no_info[myOcs.no_ninfo++] = ni->ni_opattrs;
+	nocs = myOcs.no_ninfo;
+
+	oclist = (NdbOcInfo **)op->o_tmpcalloc( 1, nocs * sizeof(NdbOcInfo *), op->o_tmpmemctx );
+
+	for ( i=0, j=0; i<myOcs.no_ninfo; i++ ) {
+		ndb_oc_get( op, myOcs.no_info[i], &j, &nocs, &oclist );
+	}
+
+	nocs = j;
+	nattrs = 0;
+	for ( i=0; i<nocs; i++ )
+		nattrs += oclist[i]->no_nattrs;
+
+	ocx = (int *)op->o_tmpalloc( nocs * sizeof(int), op->o_tmpmemctx );
+
+	attrs = (char **)op->o_tmpalloc( nattrs * sizeof(char *), op->o_tmpmemctx );
+
+	myop = (NdbIndexScanOperation **)op->o_tmpalloc( nattrs * sizeof(NdbIndexScanOperation *), op->o_tmpmemctx );
+
+	k = 0;
+	ptr = abuf;
+	for ( i=0; i<nocs; i++ ) {
+		oci = oclist[i];
+
+		myop[i] = NA->txn->getNdbIndexScanOperation( "PRIMARY", oci->no_table.bv_val );
+		if ( !myop[i] )
+			goto leave;
+		if ( myop[i]->readTuples( update ? NdbOperation::LM_Exclusive : NdbOperation::LM_CommittedRead ))
+			goto leave;
+		if ( myop[i]->setBound( 0U, NdbIndexScanOperation::BoundEQ, &eid ))
+			goto leave;
+
+		for ( j=0; j<oci->no_nattrs; j++ ) {
+			if ( oci->no_attrs[j]->na_oi != oci )
+				continue;
+			if ( oci->no_attrs[j]->na_flag & NDB_INFO_ATBLOB ) {
+				NdbBlob *bi = myop[i]->getBlobHandle( oci->no_attrs[j]->na_column );
+				attrs[k++] = (char *)bi;
+			} else {
+				attrs[k] = ptr;
+				*ptr++ = 0;
+				if ( oci->no_attrs[j]->na_len > 255 )
+					*ptr++ = 0;
+				ptr += oci->no_attrs[j]->na_len + 1;
+				myop[i]->getValue( oci->no_attrs[j]->na_column, attrs[k++] );
+			}
+		}
+		ocx[i] = k;
+	}
+	/* Must use IgnoreError, because an entry with multiple objectClasses may not
+	 * actually have attributes defined in each class / table.
+	 */
+	if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1) < 0 )
+		goto leave;
+
+	/* count results */
+	for ( i=0; i<nocs; i++ ) {
+		if (( j = myop[i]->nextResult(true) )) {
+			if ( j < 0 ) {
+				Debug( LDAP_DEBUG_TRACE,
+					"ndb_entry_get_data: first nextResult(%d) failed: %s (%d)\n",
+					i, myop[i]->getNdbError().message, myop[i]->getNdbError().code );
+			}
+			myop[i] = NULL;
+		}
+	}
+
+	nattrs = 0;
+	k = 0;
+	for ( i=0; i<nocs; i++ ) {
+		oci = oclist[i];
+		for ( j=0; j<oci->no_nattrs; j++ ) {
+			unsigned char *buf;
+			int len;
+			if ( oci->no_attrs[j]->na_oi != oci )
+				continue;
+			if ( !myop[i] ) {
+				attrs[k] = NULL;
+			} else if ( oci->no_attrs[j]->na_flag & NDB_INFO_ATBLOB ) {
+				void *vi = attrs[k];
+				NdbBlob *bi = (NdbBlob *)vi;
+				int isNull;
+				bi->getNull( isNull );
+				if ( !isNull ) {
+					nattrs++;
+				} else {
+					attrs[k] = NULL;
+				}
+			} else {
+				buf = (unsigned char *)attrs[k];
+				len = buf[0];
+				if ( oci->no_attrs[j]->na_len > 255 ) {
+					/* MedVar */
+					len |= (buf[1] << 8);
+				}
+				if ( len ) {
+					nattrs++;
+				} else {
+					attrs[k] = NULL;
+				}
+			}
+			k++;
+		}
+	}
+
+	a = attrs_alloc( nattrs+1 );
+	NA->e->e_attrs = a;
+
+	a->a_desc = slap_schema.si_ad_objectClass;
+	a->a_vals = NULL;
+	ber_bvarray_dup_x( &a->a_vals, NA->ocs, NULL );
+	a->a_nvals = a->a_vals;
+	a->a_numvals = myOcs.no_ntext;
+
+	BER_BVZERO( &bv[1] );
+
+	do {
+		a = NA->e->e_attrs->a_next;
+		k = 0;
+		for ( i=0; i<nocs; k=ocx[i], i++ ) {
+			oci = oclist[i];
+			for ( j=0; j<oci->no_nattrs; j++ ) {
+				unsigned char *buf;
+				struct berval nbv;
+				if ( oci->no_attrs[j]->na_oi != oci )
+					continue;
+				buf = (unsigned char *)attrs[k++];
+				if ( !buf )
+					continue;
+				if ( !myop[i] ) {
+					a=a->a_next;
+					continue;
+				}
+				if ( oci->no_attrs[j]->na_flag & NDB_INFO_ATBLOB ) {
+					void *vi = (void *)buf;
+					NdbBlob *bi = (NdbBlob *)vi;
+					Uint64 len;
+					Uint32 len2;
+					int isNull;
+					bi->getNull( isNull );
+					if ( isNull ) {
+						a = a->a_next;
+						continue;
+					}
+					bi->getLength( len );
+					bv[0].bv_len = len;
+					bv[0].bv_val = (char *)ch_malloc( len+1 );
+					len2 = len;
+					if ( bi->readData( bv[0].bv_val, len2 )) {
+						Debug( LDAP_DEBUG_TRACE,
+							"ndb_entry_get_data: blob readData failed: %s (%d), len %d\n",
+							bi->getNdbError().message, bi->getNdbError().code, len2 );
+					}
+					bv[0].bv_val[len] = '\0';
+					ber_bvarray_add_x( &a->a_vals, bv, NULL );
+				} else {
+					bv[0].bv_len = buf[0];
+					if ( oci->no_attrs[j]->na_len > 255 ) {
+						/* MedVar */
+						bv[0].bv_len |= (buf[1] << 8);
+						bv[0].bv_val = (char *)buf+2;
+						buf[1] = 0;
+					} else {
+						bv[0].bv_val = (char *)buf+1;
+					}
+					buf[0] = 0;
+					if ( bv[0].bv_len == 0 ) {
+						a = a->a_next;
+						continue;
+					}
+					bv[0].bv_val[bv[0].bv_len] = '\0';
+					value_add_one( &a->a_vals, bv );
+				}
+				a->a_desc = oci->no_attrs[j]->na_desc;
+				attr_normalize_one( a->a_desc, bv, &nbv, NULL );
+				a->a_numvals++;
+				if ( !BER_BVISNULL( &nbv )) {
+					ber_bvarray_add_x( &a->a_nvals, &nbv, NULL );
+				} else if ( !a->a_nvals ) {
+					a->a_nvals = a->a_vals;
+				}
+				a = a->a_next;
+			}
+		}
+		k = 0;
+		for ( i=0; i<nocs; i++ ) {
+			if ( !myop[i] )
+				continue;
+			if ((j = myop[i]->nextResult(true))) {
+				if ( j < 0 ) {
+					Debug( LDAP_DEBUG_TRACE,
+						"ndb_entry_get_data: last nextResult(%d) failed: %s (%d)\n",
+						i, myop[i]->getNdbError().message, myop[i]->getNdbError().code );
+				}
+				myop[i] = NULL;
+			} else {
+				k = 1;
+			}
+		}
+	} while ( k );
+
+	rc = 0;
+leave:
+	if ( myop ) {
+		op->o_tmpfree( myop, op->o_tmpmemctx );
+	}
+	if ( attrs ) {
+		op->o_tmpfree( attrs, op->o_tmpmemctx );
+	}
+	if ( ocx ) {
+		op->o_tmpfree( ocx, op->o_tmpmemctx );
+	}
+	if ( oclist ) {
+		op->o_tmpfree( oclist, op->o_tmpmemctx );
+	}
+
+	return rc;
+}
+
+static int
+ndb_oc_del( 
+	NdbTransaction *txn, Uint64 eid, NdbOcInfo *no )
+{
+	NdbIndexScanOperation *myop;
+	int i, rc;
+
+	for ( i=0; i<no->no_nsets; i++ ) {
+		rc = ndb_oc_del( txn, eid, no->no_sets[i] );
+		if ( rc ) return rc;
+	}
+
+	myop = txn->getNdbIndexScanOperation( "PRIMARY", no->no_table.bv_val );
+	if ( !myop )
+		return LDAP_OTHER;
+	if ( myop->readTuples( NdbOperation::LM_Exclusive ))
+		return LDAP_OTHER;
+	if ( myop->setBound( 0U, NdbIndexScanOperation::BoundEQ, &eid ))
+		return LDAP_OTHER;
+
+	txn->execute(NoCommit);
+	while ( myop->nextResult(true) == 0) {
+		do {
+			myop->deleteCurrentTuple();
+		} while (myop->nextResult(false) == 0);
+		txn->execute(NoCommit);
+	}
+
+	return 0;
+}
+
+extern "C" int
+ndb_entry_del_data(
+	BackendDB *be,
+	NdbArgs *NA
+)
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+	Uint64 eid = NA->e->e_id;
+	int i;
+	NdbOcs myOcs;
+
+	ndb_oc_check( be, NA->ndb, NA->ocs, &myOcs );
+	myOcs.no_info[myOcs.no_ninfo++] = ni->ni_opattrs;
+
+	for ( i=0; i<myOcs.no_ninfo; i++ ) {
+		if ( ndb_oc_del( NA->txn, eid, myOcs.no_info[i] ))
+			return LDAP_OTHER;
+	}
+
+	return 0;
+}
+
+extern "C" int
+ndb_dn2rdns(
+	struct berval *dn,
+	NdbRdns *rdns
+)
+{
+	char *beg, *end;
+	int i, len;
+
+	/* Walk thru RDNs */
+	end = dn->bv_val + dn->bv_len;
+	for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+		for ( beg = end-1; beg > dn->bv_val; beg-- ) {
+			if (*beg == ',') {
+				beg++;
+				break;
+			}
+		}
+		if ( beg >= dn->bv_val ) {
+			len = end - beg;
+			/* RDN is too long */
+			if ( len > NDB_RDN_LEN )
+				return LDAP_CONSTRAINT_VIOLATION;
+			memcpy( rdns->nr_buf[i]+1, beg, len );
+		} else {
+			break;
+		}
+		rdns->nr_buf[i][0] = len;
+		end = beg - 1;
+	}
+	/* Too many RDNs in DN */
+	if ( i == NDB_MAX_RDNS && beg > dn->bv_val ) {
+			return LDAP_CONSTRAINT_VIOLATION;
+	}
+	rdns->nr_num = i;
+	return 0;
+}
+
+static int
+ndb_rdns2keys(
+	NdbOperation *myop,
+	NdbRdns *rdns
+)
+{
+	int i;
+	char dummy[2] = {0,0};
+
+	/* Walk thru RDNs */
+	for ( i=0; i<rdns->nr_num; i++ ) {
+		if ( myop->equal( i+RDN_COLUMN, rdns->nr_buf[i] ))
+			return LDAP_OTHER;
+	}
+	for ( ; i<NDB_MAX_RDNS; i++ ) {
+		if ( myop->equal( i+RDN_COLUMN, dummy ))
+			return LDAP_OTHER;
+	}
+	return 0;
+}
+
+/* Store the DN2ID_TABLE fields */
+extern "C" int
+ndb_entry_put_info(
+	BackendDB *be,
+	NdbArgs *NA,
+	int update
+)
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+	const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+	const NdbDictionary::Table *myTable = myDict->getTable( DN2ID_TABLE );
+	NdbOperation *myop;
+	NdbAttrInfo *ai;
+	Attribute *aoc, *a;
+
+	/* Get the entry's objectClass attribute; it's ok to be
+	 * absent on a fresh insert
+	 */
+	aoc = attr_find( NA->e->e_attrs, slap_schema.si_ad_objectClass );
+	if ( update && !aoc )
+		return LDAP_OBJECT_CLASS_VIOLATION;
+
+	myop = NA->txn->getNdbOperation( myTable );
+	if ( !myop )
+		return LDAP_OTHER;
+	if ( update ) {
+		if ( myop->updateTuple())
+			return LDAP_OTHER;
+	} else {
+		if ( myop->insertTuple())
+			return LDAP_OTHER;
+	}
+
+	if ( ndb_rdns2keys( myop, NA->rdns ))
+		return LDAP_OTHER;
+
+	/* Set entry ID */
+	{
+		Uint64 eid = NA->e->e_id;
+		if ( myop->setValue( EID_COLUMN, eid ))
+			return LDAP_OTHER;
+	}
+
+	/* Set list of objectClasses */
+	/* List is <sp> <class> <sp> <class> <sp> ... so that
+	 * searches for " class " will yield accurate results
+	 */
+	if ( aoc ) {
+		char *ptr, buf[sizeof(MedVar)];
+		NdbOcs myOcs;
+		int i;
+
+		ndb_oc_check( be, NA->ndb, aoc->a_nvals, &myOcs );
+		ptr = buf+2;
+		*ptr++ = ' ';
+		for ( i=0; i<myOcs.no_ntext; i++ ) {
+			/* data loss... */
+			if ( ptr + myOcs.no_text[i].bv_len + 1 >= &buf[sizeof(buf)] )
+				break;
+			ptr = lutil_strcopy( ptr, myOcs.no_text[i].bv_val );
+			*ptr++ = ' ';
+		}
+
+		/* implicit classes */
+		if ( myOcs.no_nitext ) {
+			*ptr++ = '@';
+			*ptr++ = ' ';
+			for ( i=0; i<myOcs.no_nitext; i++ ) {
+				/* data loss... */
+				if ( ptr + myOcs.no_itext[i].bv_len + 1 >= &buf[sizeof(buf)] )
+					break;
+				ptr = lutil_strcopy( ptr, myOcs.no_itext[i].bv_val );
+				*ptr++ = ' ';
+			}
+		}
+
+		i = ptr - buf - 2;
+		buf[0] = i & 0xff;
+		buf[1] = i >> 8;
+		if ( myop->setValue( OCS_COLUMN, buf ))
+			return LDAP_OTHER;
+	}
+
+	/* Set any indexed attrs */
+	for ( a = NA->e->e_attrs; a; a=a->a_next ) {
+		ai = ndb_ai_find( ni, a->a_desc->ad_type );
+		if ( ai && ( ai->na_flag & NDB_INFO_INDEX )) {
+			char *ptr, buf[sizeof(MedVar)];
+			int len;
+
+			ptr = buf+1;
+			len = a->a_vals[0].bv_len;
+			/* FIXME: data loss */
+			if ( len > ai->na_len )
+				len = ai->na_len;
+			buf[0] = len & 0xff;
+			if ( ai->na_len > 255 ) {
+				*ptr++ = len >> 8;
+			}
+			memcpy( ptr, a->a_vals[0].bv_val, len );
+			if ( myop->setValue( ai->na_ixcol, buf ))
+				return LDAP_OTHER;
+		}
+	}
+
+	return 0;
+}
+
+extern "C" struct berval *
+ndb_str2bvarray(
+	char *str,
+	int len,
+	char delim,
+	void *ctx
+)
+{
+	struct berval *list, tmp;
+	char *beg;
+	int i, num;
+
+	while ( *str == delim ) {
+		str++;
+		len--;
+	}
+
+	while ( str[len-1] == delim ) {
+		str[--len] = '\0';
+	}
+
+	for ( i = 1, beg = str;; i++ ) {
+		beg = strchr( beg, delim );
+		if ( !beg )
+			break;
+		if ( beg >= str + len )
+			break;
+		beg++;
+	}
+
+	num = i;
+	list = (struct berval *)slap_sl_malloc( (num+1)*sizeof(struct berval), ctx);
+
+	for ( i = 0, beg = str; i<num; i++ ) {
+		tmp.bv_val = beg;
+		beg = strchr( beg, delim );
+		if ( beg >= str + len )
+			beg = NULL;
+		if ( beg ) {
+			tmp.bv_len = beg - tmp.bv_val;
+		} else {
+			tmp.bv_len = len - (tmp.bv_val - str);
+		}
+		ber_dupbv_x( &list[i], &tmp, ctx );
+		beg++;
+	}
+
+	BER_BVZERO( &list[i] );
+	return list;
+}
+
+extern "C" struct berval *
+ndb_ref2oclist(
+	const char *ref,
+	void *ctx
+)
+{
+	char *implied;
+
+	/* MedVar */
+	int len = ref[0] | (ref[1] << 8);
+
+	/* don't return the implied classes */
+	implied = (char *)memchr( ref+2, '@', len );
+	if ( implied ) {
+		len = implied - ref - 2;
+		*implied = '\0';
+	}
+
+	return ndb_str2bvarray( (char *)ref+2, len, ' ', ctx );
+}
+
+/* Retrieve the DN2ID_TABLE fields. Can call with NULL ocs if just verifying
+ * the existence of a DN.
+ */
+extern "C" int
+ndb_entry_get_info(
+	Operation *op,
+	NdbArgs *NA,
+	int update,
+	struct berval *matched
+)
+{
+	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+	const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+	const NdbDictionary::Table *myTable = myDict->getTable( DN2ID_TABLE );
+	NdbOperation *myop[NDB_MAX_RDNS];
+	NdbRecAttr *eid[NDB_MAX_RDNS], *oc[NDB_MAX_RDNS];
+	char idbuf[NDB_MAX_RDNS][2*sizeof(ID)];
+	char ocbuf[NDB_MAX_RDNS][NDB_OC_BUFLEN];
+
+	if ( matched ) {
+		BER_BVZERO( matched );
+	}
+	if ( !myTable ) {
+		return LDAP_OTHER;
+	}
+
+	myop[0] = NA->txn->getNdbOperation( myTable );
+	if ( !myop[0] ) {
+		return LDAP_OTHER;
+	}
+
+	if ( myop[0]->readTuple( update ? NdbOperation::LM_Exclusive : NdbOperation::LM_CommittedRead )) {
+		return LDAP_OTHER;
+	}
+
+	if ( !NA->rdns->nr_num && ndb_dn2rdns( &NA->e->e_name, NA->rdns )) {
+		return LDAP_NO_SUCH_OBJECT;
+	}
+
+	if ( ndb_rdns2keys( myop[0], NA->rdns )) {
+		return LDAP_OTHER;
+	}
+
+	eid[0] = myop[0]->getValue( EID_COLUMN, idbuf[0] );
+	if ( !eid[0] ) {
+		return LDAP_OTHER;
+	}
+
+	ocbuf[0][0] = 0;
+	ocbuf[0][1] = 0;
+	if ( !NA->ocs ) {
+		oc[0] = myop[0]->getValue( OCS_COLUMN, ocbuf[0] );
+		if ( !oc[0] ) {
+			return LDAP_OTHER;
+		}
+	}
+
+	if ( NA->txn->execute(NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1) < 0 ) {
+		return LDAP_OTHER;
+	}
+
+	switch( myop[0]->getNdbError().code ) {
+	case 0:
+		if ( !eid[0]->isNULL() && ( NA->e->e_id = eid[0]->u_64_value() )) {
+			/* If we didn't care about OCs, or we got them */
+			if ( NA->ocs || ocbuf[0][0] || ocbuf[0][1] ) {
+				/* If wanted, return them */
+				if ( !NA->ocs )
+					NA->ocs = ndb_ref2oclist( ocbuf[0], op->o_tmpmemctx );
+				break;
+			}
+		}
+		/* FALLTHRU */
+	case NDB_NO_SUCH_OBJECT:	/* no such tuple: look for closest parent */
+		if ( matched ) {
+			int i, j, k;
+			char dummy[2] = {0,0};
+
+			/* get to last RDN, then back up 1 */
+			k = NA->rdns->nr_num - 1;
+
+			for ( i=0; i<k; i++ ) {
+				myop[i] = NA->txn->getNdbOperation( myTable );
+				if ( !myop[i] )
+					return LDAP_OTHER;
+				if ( myop[i]->readTuple( NdbOperation::LM_CommittedRead ))
+					return LDAP_OTHER;
+				for ( j=0; j<=i; j++ ) {
+					if ( myop[i]->equal( j+RDN_COLUMN, NA->rdns->nr_buf[j] ))
+						return LDAP_OTHER;
+				}
+				for ( ;j<NDB_MAX_RDNS; j++ ) {
+					if ( myop[i]->equal( j+RDN_COLUMN, dummy ))
+						return LDAP_OTHER;
+				}
+				eid[i] = myop[i]->getValue( EID_COLUMN, idbuf[i] );
+				if ( !eid[i] ) {
+					return LDAP_OTHER;
+				}
+				ocbuf[i][0] = 0;
+				ocbuf[i][1] = 0;
+				if ( !NA->ocs ) {
+					oc[i] = myop[0]->getValue( OCS_COLUMN, ocbuf[i] );
+					if ( !oc[i] ) {
+						return LDAP_OTHER;
+					}
+				}
+			}
+			if ( NA->txn->execute(NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1) < 0 ) {
+				return LDAP_OTHER;
+			}
+			for ( --i; i>=0; i-- ) {
+				if ( myop[i]->getNdbError().code == 0 ) {
+					for ( j=0; j<=i; j++ )
+						matched->bv_len += NA->rdns->nr_buf[j][0];
+					NA->erdns = NA->rdns->nr_num;
+					NA->rdns->nr_num = j;
+					matched->bv_len += i;
+					matched->bv_val = NA->e->e_name.bv_val +
+						NA->e->e_name.bv_len - matched->bv_len;
+					if ( !eid[i]->isNULL() )
+						NA->e->e_id = eid[i]->u_64_value();
+					if ( !NA->ocs )
+						NA->ocs = ndb_ref2oclist( ocbuf[i], op->o_tmpmemctx );
+					break;
+				}
+			}
+		}
+		return LDAP_NO_SUCH_OBJECT;
+	default:
+		return LDAP_OTHER;
+	}
+
+	return 0;
+}
+
+extern "C" int
+ndb_entry_del_info(
+	BackendDB *be,
+	NdbArgs *NA
+)
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+	const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+	const NdbDictionary::Table *myTable = myDict->getTable( DN2ID_TABLE );
+	NdbOperation *myop;
+
+	myop = NA->txn->getNdbOperation( myTable );
+	if ( !myop )
+		return LDAP_OTHER;
+	if ( myop->deleteTuple())
+		return LDAP_OTHER;
+
+	if ( ndb_rdns2keys( myop, NA->rdns ))
+		return LDAP_OTHER;
+
+	return 0;
+}
+
+extern "C" int
+ndb_next_id(
+	BackendDB *be,
+	Ndb *ndb,
+	ID *id
+)
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+	const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
+	const NdbDictionary::Table *myTable = myDict->getTable( NEXTID_TABLE );
+	Uint64 nid = 0;
+	int rc;
+
+	if ( !myTable ) {
+		Debug( LDAP_DEBUG_ANY, "ndb_next_id: " NEXTID_TABLE " table is missing\n",
+			0, 0, 0 );
+		return LDAP_OTHER;
+	}
+
+	rc = ndb->getAutoIncrementValue( myTable, nid, 1000 );
+	if ( !rc )
+		*id = nid;
+	return rc;
+}
+
+extern "C" { static void ndb_thread_hfree( void *key, void *data ); };
+static void
+ndb_thread_hfree( void *key, void *data )
+{
+	Ndb *ndb = (Ndb *)data;
+	delete ndb;
+}
+
+extern "C" int
+ndb_thread_handle(
+	Operation *op,
+	Ndb **ndb )
+{
+	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+	void *data;
+
+	if ( ldap_pvt_thread_pool_getkey( op->o_threadctx, ni, &data, NULL )) {
+		Ndb *myNdb;
+		int rc;
+		ldap_pvt_thread_mutex_lock( &ni->ni_conn_mutex );
+		myNdb = new Ndb( ni->ni_cluster[ni->ni_nextconn++], ni->ni_dbname );
+		if ( ni->ni_nextconn >= ni->ni_nconns )
+			ni->ni_nextconn = 0;
+		ldap_pvt_thread_mutex_unlock( &ni->ni_conn_mutex );
+		if ( !myNdb ) {
+			return LDAP_OTHER;
+		}
+		rc = myNdb->init(1024);
+		if ( rc ) {
+			delete myNdb;
+			Debug( LDAP_DEBUG_ANY, "ndb_thread_handle: err %d\n",
+				rc, 0, 0 );
+			return rc;
+		}
+		data = (void *)myNdb;
+		if (( rc = ldap_pvt_thread_pool_setkey( op->o_threadctx, ni,
+			data, ndb_thread_hfree, NULL, NULL ))) {
+			delete myNdb;
+			Debug( LDAP_DEBUG_ANY, "ndb_thread_handle: err %d\n",
+				rc, 0, 0 );
+			return rc;
+		}
+	}
+	*ndb = (Ndb *)data;
+	return 0;
+}
+
+extern "C" int
+ndb_entry_get(
+	Operation *op,
+	struct berval *ndn,
+	ObjectClass *oc,
+	AttributeDescription *ad,
+	int rw,
+	Entry **ent )
+{
+	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+	NdbArgs NA;
+	Entry e = {0};
+	int rc;
+
+	/* Get our NDB handle */
+	rc = ndb_thread_handle( op, &NA.ndb );
+
+	NA.txn = NA.ndb->startTransaction();
+	if( !NA.txn ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_entry_get) ": startTransaction failed: %s (%d)\n",
+			NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+		return 1;
+	}
+
+	e.e_name = *ndn;
+	NA.e = &e;
+	/* get entry */
+	{
+		NdbRdns rdns;
+		rdns.nr_num = 0;
+		NA.ocs = NULL;
+		NA.rdns = &rdns;
+		rc = ndb_entry_get_info( op, &NA, rw, NULL );
+	}
+	if ( rc == 0 ) {
+		e.e_name = *ndn;
+		e.e_nname = *ndn;
+		rc = ndb_entry_get_data( op, &NA, 0 );
+		ber_bvarray_free( NA.ocs );
+		if ( rc == 0 ) {
+			if ( oc && !is_entry_objectclass_or_sub( &e, oc )) {
+				attrs_free( e.e_attrs );
+				rc = 1;
+			}
+		}
+	}
+	if ( rc == 0 ) {
+		*ent = entry_alloc();
+		**ent = e;
+		ber_dupbv( &(*ent)->e_name, ndn );
+		ber_dupbv( &(*ent)->e_nname, ndn );
+	} else {
+		rc = 1;
+	}
+	NA.txn->close();
+	return rc;
+}
+
+/* Congestion avoidance code
+ * for Deadlock Rollback
+ */
+
+extern "C" void
+ndb_trans_backoff( int num_retries )
+{
+	int i;
+	int delay = 0;
+	int pow_retries = 1;
+	unsigned long key = 0;
+	unsigned long max_key = -1;
+	struct timeval timeout;
+
+	lutil_entropy( (unsigned char *) &key, sizeof( unsigned long ));
+
+	for ( i = 0; i < num_retries; i++ ) {
+		if ( i >= 5 ) break;
+		pow_retries *= 4;
+	}
+
+	delay = 16384 * (key * (double) pow_retries / (double) max_key);
+	delay = delay ? delay : 1;
+
+	Debug( LDAP_DEBUG_TRACE,  "delay = %d, num_retries = %d\n", delay, num_retries, 0 );
+
+	timeout.tv_sec = delay / 1000000;
+	timeout.tv_usec = delay % 1000000;
+	select( 0, NULL, NULL, NULL, &timeout );
+}
+
+extern "C" void
+ndb_check_referral( Operation *op, SlapReply *rs, NdbArgs *NA )
+{
+	struct berval dn, ndn;
+	int i, dif;
+	dif = NA->erdns - NA->rdns->nr_num;
+
+	/* Set full DN of matched into entry */
+	for ( i=0; i<dif; i++ ) {
+		dnParent( &NA->e->e_name, &dn );
+		dnParent( &NA->e->e_nname, &ndn );
+		NA->e->e_name = dn;
+		NA->e->e_nname = ndn;
+	}
+
+	/* return referral only if "disclose" is granted on the object */
+	if ( access_allowed( op, NA->e, slap_schema.si_ad_entry,
+		NULL, ACL_DISCLOSE, NULL )) {
+		Attribute a;
+		for ( i=0; !BER_BVISNULL( &NA->ocs[i] ); i++ );
+		a.a_numvals = i;
+		a.a_desc = slap_schema.si_ad_objectClass;
+		a.a_vals = NA->ocs;
+		a.a_nvals = NA->ocs;
+		a.a_next = NULL;
+		NA->e->e_attrs = &a;
+		if ( is_entry_referral( NA->e )) {
+			NA->e->e_attrs = NULL;
+			ndb_entry_get_data( op, NA, 0 );
+			rs->sr_ref = get_entry_referrals( op, NA->e );
+			if ( rs->sr_ref ) {
+				rs->sr_err = LDAP_REFERRAL;
+				rs->sr_flags |= REP_REF_MUSTBEFREED;
+			}
+			attrs_free( NA->e->e_attrs );
+		}
+		NA->e->e_attrs = NULL;
+	}
+}
diff --git a/servers/slapd/back-ndb/proto-ndb.h b/servers/slapd/back-ndb/proto-ndb.h
new file mode 100644
index 0000000000000000000000000000000000000000..bda94bb929791b64edb9cb8bae481c093bc68a50
--- /dev/null
+++ b/servers/slapd/back-ndb/proto-ndb.h
@@ -0,0 +1,166 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#ifndef _PROTO_NDB_H
+#define _PROTO_NDB_H
+
+LDAP_BEGIN_DECL
+
+extern BI_init		ndb_back_initialize;
+
+extern BI_open		ndb_back_open;
+extern BI_close		ndb_back_close;
+extern BI_destroy	ndb_back_destroy;
+
+extern BI_db_init	ndb_back_db_init;
+extern BI_db_destroy	ndb_back_db_destroy;
+
+extern BI_op_bind	ndb_back_bind;
+extern BI_op_unbind	ndb_back_unbind;
+extern BI_op_search	ndb_back_search;
+extern BI_op_compare	ndb_back_compare;
+extern BI_op_modify	ndb_back_modify;
+extern BI_op_modrdn	ndb_back_modrdn;
+extern BI_op_add	ndb_back_add;
+extern BI_op_delete	ndb_back_delete;
+
+extern BI_operational	ndb_operational;
+extern BI_has_subordinates	ndb_has_subordinates;
+extern BI_entry_get_rw	ndb_entry_get;
+
+extern BI_tool_entry_open	ndb_tool_entry_open;
+extern BI_tool_entry_close	ndb_tool_entry_close;
+extern BI_tool_entry_first	ndb_tool_entry_first;
+extern BI_tool_entry_next	ndb_tool_entry_next;
+extern BI_tool_entry_get	ndb_tool_entry_get;
+extern BI_tool_entry_put	ndb_tool_entry_put;
+extern BI_tool_dn2id_get	ndb_tool_dn2id_get;
+
+extern int ndb_modify_internal(
+	Operation *op,
+	NdbArgs *NA,
+	const char **text,
+	char *textbuf,
+	size_t textlen );
+
+extern int
+ndb_entry_get_data(
+	Operation *op,
+	NdbArgs *args,
+	int update );
+
+extern int
+ndb_entry_put_data(
+	BackendDB *be,
+	NdbArgs *args );
+
+extern int
+ndb_entry_del_data(
+	BackendDB *be,
+	NdbArgs *args );
+
+extern int
+ndb_entry_put_info(
+	BackendDB *be,
+	NdbArgs *args,
+	int update );
+
+extern int
+ndb_entry_get_info(
+	Operation *op,
+	NdbArgs *args,
+	int update,
+	struct berval *matched );
+
+extern "C" int
+ndb_entry_del_info(
+	BackendDB *be,
+	NdbArgs *args );
+
+extern int
+ndb_dn2rdns(
+	struct berval *dn,
+	NdbRdns *buf );
+
+extern NdbAttrInfo *
+ndb_ai_find( struct ndb_info *ni, AttributeType *at );
+
+extern NdbAttrInfo *
+ndb_ai_get( struct ndb_info *ni, struct berval *at );
+
+extern int
+ndb_aset_get( struct ndb_info *ni, struct berval *sname, struct berval *attrs, NdbOcInfo **ret );
+
+extern int
+ndb_aset_create( struct ndb_info *ni, NdbOcInfo *oci );
+
+extern int
+ndb_oc_read( struct ndb_info *ni, const NdbDictionary::Dictionary *dict );
+
+extern int
+ndb_oc_attrs(
+	NdbTransaction *txn,
+	const NdbDictionary::Table *myTable,
+	Entry *e,
+	NdbOcInfo *no,
+	NdbAttrInfo **attrs,
+	int nattrs,
+	Attribute *old );
+
+extern int
+ndb_has_children(
+	NdbArgs *NA,
+	int *hasChildren );
+
+extern struct berval *
+ndb_str2bvarray(
+	char *str,
+	int len,
+	char delim,
+	void *ctx );
+
+extern struct berval *
+ndb_ref2oclist(
+	const char *ref,
+	void *ctx );
+
+extern int
+ndb_next_id(
+	BackendDB *be,
+	Ndb *ndb,
+	ID *id );
+
+extern int
+ndb_thread_handle(
+	Operation *op,
+	Ndb **ndb );
+
+extern int
+ndb_back_init_cf(
+	BackendInfo *bi );
+
+extern "C" void
+ndb_trans_backoff( int num_retries );
+
+extern "C" void
+ndb_check_referral( Operation *op, SlapReply *rs, NdbArgs *NA );
+
+LDAP_END_DECL
+
+#endif /* _PROTO_NDB_H */
diff --git a/servers/slapd/back-ndb/search.cpp b/servers/slapd/back-ndb/search.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..87a4b6428d01c220133db418071a3f1610a8cc2d
--- /dev/null
+++ b/servers/slapd/back-ndb/search.cpp
@@ -0,0 +1,844 @@
+/* search.cpp - tools for slap tools */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+#include "lutil.h"
+
+#include "back-ndb.h"
+
+static int
+ndb_dn2bound(
+	NdbIndexScanOperation *myop,
+	NdbRdns *rdns
+)
+{
+	unsigned int i;
+
+	/* Walk thru RDNs */
+	for ( i=0; i<rdns->nr_num; i++ ) {
+		/* Note: RDN_COLUMN offset not needed here */
+		if ( myop->setBound( i, NdbIndexScanOperation::BoundEQ, rdns->nr_buf[i] ))
+			return LDAP_OTHER;
+	}
+	return i;
+}
+
+/* Check that all filter terms reside in the same table.
+ *
+ * If any of the filter terms are indexed, then only an IndexScan of the OL_index
+ * will be performed. If none are indexed, but all the terms reside in a single
+ * table, a Scan can be performed with the LDAP filter transformed into a ScanFilter.
+ *
+ * Otherwise, a full scan of the DB must be done with all filtering done by slapd.
+ */
+static int ndb_filter_check( struct ndb_info *ni, Filter *f,
+	NdbOcInfo **oci, int *indexed, int *ocfilter )
+{
+	AttributeDescription *ad = NULL;
+	ber_tag_t choice = f->f_choice;
+	int rc = 0, undef = 0;
+
+	if ( choice & SLAPD_FILTER_UNDEFINED ) {
+		choice &= SLAPD_FILTER_MASK;
+		undef = 1;
+	}
+	switch( choice ) {
+	case LDAP_FILTER_AND:
+	case LDAP_FILTER_OR:
+	case LDAP_FILTER_NOT:
+		for ( f = f->f_list; f; f=f->f_next ) {
+			rc = ndb_filter_check( ni, f, oci, indexed, ocfilter );
+			if ( rc ) return rc;
+		}
+		break;
+	case LDAP_FILTER_PRESENT:
+		ad = f->f_desc;
+		break;
+	case LDAP_FILTER_EQUALITY:
+	case LDAP_FILTER_SUBSTRINGS:
+	case LDAP_FILTER_GE:
+	case LDAP_FILTER_LE:
+	case LDAP_FILTER_APPROX:
+		ad = f->f_av_desc;
+		break;
+	default:
+		break;
+	}
+	if ( ad && !undef ) {
+		NdbAttrInfo *ai;
+		/* ObjectClass filtering is in dn2id table */
+		if ( ad == slap_schema.si_ad_objectClass ) {
+			if ( choice == LDAP_FILTER_EQUALITY )
+				(*ocfilter)++;
+			return 0;
+		}
+		ai = ndb_ai_find( ni, ad->ad_type );
+		if ( ai ) {
+			if ( ai->na_flag & NDB_INFO_INDEX )
+				(*indexed)++;
+			if ( *oci ) {
+				if ( ai->na_oi != *oci )
+					rc = -1;
+			} else {
+				*oci = ai->na_oi;
+			}
+		}
+	}
+	return rc;
+}
+
+static int ndb_filter_set( Operation *op, struct ndb_info *ni, Filter *f, int indexed,
+	NdbIndexScanOperation *scan, NdbScanFilter *sf, int *bounds )
+{
+	AttributeDescription *ad = NULL;
+	ber_tag_t choice = f->f_choice;
+	int undef = 0;
+
+	if ( choice & SLAPD_FILTER_UNDEFINED ) {
+		choice &= SLAPD_FILTER_MASK;
+		undef = 1;
+	}
+	switch( choice ) {
+	case LDAP_FILTER_NOT:
+		/* no indexing for these */
+		break;
+	case LDAP_FILTER_OR:
+		/* FIXME: these bounds aren't right. */
+		if ( indexed ) {
+			scan->end_of_bound( (*bounds)++ );
+		}
+	case LDAP_FILTER_AND:
+		if ( sf ) {
+			sf->begin( choice == LDAP_FILTER_OR ? NdbScanFilter::OR : NdbScanFilter::AND );
+		}
+		for ( f = f->f_list; f; f=f->f_next ) {
+			if ( ndb_filter_set( op, ni, f, indexed, scan, sf, bounds ))
+				return -1;
+		}
+		if ( sf ) {
+			sf->end();
+		}
+		break;
+	case LDAP_FILTER_PRESENT:
+		ad = f->f_desc;
+		break;
+	case LDAP_FILTER_EQUALITY:
+	case LDAP_FILTER_SUBSTRINGS:
+	case LDAP_FILTER_GE:
+	case LDAP_FILTER_LE:
+	case LDAP_FILTER_APPROX:
+		ad = f->f_av_desc;
+		break;
+	default:
+		break;
+	}
+	if ( ad && !undef ) {
+		NdbAttrInfo *ai;
+		/* ObjectClass filtering is in dn2id table */
+		if ( ad == slap_schema.si_ad_objectClass ) {
+			return 0;
+		}
+		ai = ndb_ai_find( ni, ad->ad_type );
+		if ( ai ) {
+			int rc;
+			if ( ai->na_flag & NDB_INFO_INDEX ) {
+				char *buf, *ptr;
+				NdbIndexScanOperation::BoundType bt;
+
+				switch(choice) {
+				case LDAP_FILTER_PRESENT:
+					rc = scan->setBound( ai->na_ixcol - IDX_COLUMN,
+						NdbIndexScanOperation::BoundGT, NULL );
+					break;
+				case LDAP_FILTER_EQUALITY:
+				case LDAP_FILTER_APPROX:
+					bt = NdbIndexScanOperation::BoundEQ;
+					goto setit;
+				case LDAP_FILTER_GE:
+					bt = NdbIndexScanOperation::BoundGE;
+					goto setit;
+				case LDAP_FILTER_LE:
+					bt = NdbIndexScanOperation::BoundLE;
+				setit:
+					rc = f->f_av_value.bv_len+1;
+					if ( ai->na_len > 255 )
+						rc++;
+					buf = (char *)op->o_tmpalloc( rc, op->o_tmpmemctx );
+					rc = f->f_av_value.bv_len;
+					buf[0] = rc & 0xff;
+					ptr = buf+1;
+					if ( ai->na_len > 255 ) {
+						buf[1] = (rc >> 8);
+						ptr++;
+					}
+					memcpy( ptr, f->f_av_value.bv_val, f->f_av_value.bv_len );
+					rc = scan->setBound( ai->na_ixcol - IDX_COLUMN, bt, buf );
+					op->o_tmpfree( buf, op->o_tmpmemctx );
+					break;
+				default:
+					break;
+				}
+			} else if ( sf ) {
+				char *buf, *ptr;
+				NdbScanFilter::BinaryCondition bc;
+
+				switch(choice) {
+				case LDAP_FILTER_PRESENT:
+					rc = sf->isnotnull( ai->na_column );
+					break;
+				case LDAP_FILTER_EQUALITY:
+				case LDAP_FILTER_APPROX:
+					bc = NdbScanFilter::COND_EQ;
+					goto setf;
+				case LDAP_FILTER_GE:
+					bc = NdbScanFilter::COND_GE;
+					goto setf;
+				case LDAP_FILTER_LE:
+					bc = NdbScanFilter::COND_LE;
+				setf:
+					rc = sf->cmp( bc, ai->na_column, f->f_av_value.bv_val, f->f_av_value.bv_len );
+					break;
+				case LDAP_FILTER_SUBSTRINGS:
+					rc = 0;
+					if ( f->f_sub_initial.bv_val )
+						rc += f->f_sub_initial.bv_len + 1;
+					if ( f->f_sub_any ) {
+						int i;
+						if ( !rc ) rc++;
+						for (i=0; f->f_sub_any[i].bv_val; i++)
+							rc += f->f_sub_any[i].bv_len + 1;
+					}
+					if ( f->f_sub_final.bv_val ) {
+						if ( !rc ) rc++;
+						rc += f->f_sub_final.bv_len;
+					}
+					buf = (char *)op->o_tmpalloc( rc+1, op->o_tmpmemctx );
+					ptr = buf;
+					if ( f->f_sub_initial.bv_val ) {
+						memcpy( ptr, f->f_sub_initial.bv_val, f->f_sub_initial.bv_len );
+						ptr += f->f_sub_initial.bv_len;
+						*ptr++ = '%';
+					}
+					if ( f->f_sub_any ) {
+						int i;
+						if ( ptr == buf )
+							*ptr++ = '%';
+						for (i=0; f->f_sub_any[i].bv_val; i++) {
+							memcpy( ptr, f->f_sub_any[i].bv_val, f->f_sub_any[i].bv_len );
+							ptr += f->f_sub_any[i].bv_len;
+							*ptr++ = '%';
+						}
+					}
+					if ( f->f_sub_final.bv_val ) {
+						if ( ptr == buf )
+							*ptr++ = '%';
+						memcpy( ptr, f->f_sub_final.bv_val, f->f_sub_final.bv_len );
+						ptr += f->f_sub_final.bv_len;
+					}
+					*ptr = '\0';
+					rc = sf->cmp( NdbScanFilter::COND_LIKE, ai->na_column, buf, ptr - buf );
+					op->o_tmpfree( buf, op->o_tmpmemctx );
+					break;
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static int ndb_oc_search( Operation *op, SlapReply *rs, Ndb *ndb, NdbTransaction *txn,
+	NdbRdns *rbase, NdbOcInfo *oci, int indexed )
+{
+	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+	const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
+	const NdbDictionary::Table *myTable;
+	const NdbDictionary::Index *myIndex;
+	NdbIndexScanOperation *scan;
+	NdbIndexOperation *ixop;
+	NdbScanFilter *sf = NULL;
+	struct berval *ocs;
+	NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
+	char dnBuf[2048], *ptr;
+	NdbRdns rdns;
+	NdbArgs NA;
+	char idbuf[2*sizeof(ID)];
+	char ocbuf[NDB_OC_BUFLEN];
+	int i, rc, bounds;
+	Entry e = {0};
+	Uint64 eid;
+	time_t stoptime;
+	int manageDSAit;
+
+	stoptime = op->o_time + op->ors_tlimit;
+	manageDSAit = get_manageDSAit( op );
+
+	myTable = myDict->getTable( oci->no_table.bv_val );
+	if ( indexed ) { 
+		scan = txn->getNdbIndexScanOperation( INDEX_NAME, DN2ID_TABLE );
+		if ( !scan )
+			return LDAP_OTHER;
+		scan->readTuples( NdbOperation::LM_CommittedRead );
+	} else {
+		myIndex = myDict->getIndex( "eid$unique", DN2ID_TABLE );
+		if ( !myIndex ) {
+			Debug( LDAP_DEBUG_ANY, DN2ID_TABLE " eid index is missing!\n", 0, 0, 0 );
+			rs->sr_err = LDAP_OTHER;
+			goto leave;
+		}
+		scan = (NdbIndexScanOperation *)txn->getNdbScanOperation( myTable );
+		if ( !scan )
+			return LDAP_OTHER;
+		scan->readTuples( NdbOperation::LM_CommittedRead );
+#if 1
+		sf = new NdbScanFilter(scan);
+		if ( !sf )
+			return LDAP_OTHER;
+		switch ( op->ors_filter->f_choice ) {
+		case LDAP_FILTER_AND:
+		case LDAP_FILTER_OR:
+		case LDAP_FILTER_NOT:
+			break;
+		default:
+			if ( sf->begin() < 0 ) {
+				rc = LDAP_OTHER;
+				goto leave;
+			}
+		}
+#endif
+	}
+
+	bounds = 0;
+	rc = ndb_filter_set( op, ni, op->ors_filter, indexed, scan, sf, &bounds );
+	if ( rc )
+		goto leave;
+	if ( sf ) sf->end();
+	
+	scanID = scan->getValue( EID_COLUMN, idbuf );
+	if ( indexed ) {
+		scanOC = scan->getValue( OCS_COLUMN, ocbuf );
+		for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+			rdns.nr_buf[i][0] = '\0';
+			scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
+		}
+	}
+
+	if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
+		rs->sr_err = LDAP_OTHER;
+		goto leave;
+	}
+
+	e.e_name.bv_val = dnBuf;
+	NA.e = &e;
+	NA.ndb = ndb;
+	while ( scan->nextResult( true, true ) == 0 ) {
+		NdbTransaction *tx2;
+		if ( op->o_abandon ) {
+			rs->sr_err = SLAPD_ABANDON;
+			break;
+		}
+		if ( slapd_shutdown ) {
+			rs->sr_err = LDAP_UNAVAILABLE;
+			break;
+		}
+		if ( op->ors_tlimit != SLAP_NO_LIMIT &&
+			slap_get_time() > stoptime ) {
+			rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+			break;
+		}
+
+		eid = scanID->u_64_value();
+		e.e_id = eid;
+		if ( !indexed ) {
+			tx2 = ndb->startTransaction( myTable );
+			if ( !tx2 ) {
+				rs->sr_err = LDAP_OTHER;
+				goto leave;
+			}
+
+			ixop = tx2->getNdbIndexOperation( myIndex );
+			if ( !ixop ) {
+				tx2->close();
+				rs->sr_err = LDAP_OTHER;
+				goto leave;
+			}
+			ixop->readTuple( NdbOperation::LM_CommittedRead );
+			ixop->equal( EID_COLUMN, eid );
+
+			scanOC = ixop->getValue( OCS_COLUMN, ocbuf );
+			for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+				rdns.nr_buf[i][0] = '\0';
+				scanDN[i] = ixop->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
+			}
+			rc = tx2->execute( NdbTransaction::Commit, NdbOperation::AbortOnError, 1 );
+			tx2->close();
+			if ( rc ) {
+				rs->sr_err = LDAP_OTHER;
+				goto leave;
+			}
+		}
+
+		ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
+		for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+			if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
+				break;
+		}
+		rdns.nr_num = i;
+
+		/* entry must be subordinate to the base */
+		if ( i < rbase->nr_num ) {
+			continue;
+		}
+
+		ptr = dnBuf;
+		for ( --i; i>=0; i-- ) {
+			char *buf;
+			int len;
+			buf = rdns.nr_buf[i];
+			len = *buf++;
+			ptr = lutil_strncopy( ptr, buf, len );
+			if ( i ) *ptr++ = ',';
+		}
+		*ptr = '\0';
+		e.e_name.bv_len = ptr - dnBuf;
+
+		/* More scope checks */
+		/* If indexed, these can be moved into the ScanFilter */
+		switch( op->ors_scope ) {
+		case LDAP_SCOPE_ONELEVEL:
+			if ( rdns.nr_num != rbase->nr_num+1 )
+				continue;
+		case LDAP_SCOPE_SUBORDINATE:
+			if ( rdns.nr_num == rbase->nr_num )
+				continue;
+		case LDAP_SCOPE_SUBTREE:
+		default:
+			if ( e.e_name.bv_len <= op->o_req_dn.bv_len ) {
+				if ( op->ors_scope != LDAP_SCOPE_SUBTREE ||
+					strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val ))
+					continue;
+			} else if ( strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val +
+				e.e_name.bv_len - op->o_req_dn.bv_len ))
+				continue;
+		}
+
+		dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
+		{
+#ifdef notdef		/* NDBapi is broken here */
+			Ndb::Key_part_ptr keys[2];
+			char xbuf[32];
+			keys[0].ptr = &eid;
+			keys[0].len = sizeof(eid);
+			keys[1].ptr = NULL;
+			keys[1].len = 0;
+			tx2 = ndb->startTransaction( myTable, keys, xbuf, sizeof(xbuf));
+#else
+			tx2 = ndb->startTransaction( myTable );
+#endif
+			if ( !tx2 ) {
+				rs->sr_err = LDAP_OTHER;
+				goto leave;
+			}
+			NA.txn = tx2;
+			NA.ocs = ocs;
+			rc = ndb_entry_get_data( op, &NA, 0 );
+			tx2->close();
+		}
+		ber_bvarray_free_x( ocs, op->o_tmpmemctx );
+		if ( !manageDSAit && is_entry_referral( &e )) {
+			BerVarray erefs = get_entry_referrals( op, &e );
+			rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
+				op->ors_scope == LDAP_SCOPE_ONELEVEL ?
+					LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
+			rc = send_search_reference( op, rs );
+			ber_bvarray_free( rs->sr_ref );
+			ber_bvarray_free( erefs );
+			rs->sr_ref = NULL;
+		} else if ( manageDSAit || !is_entry_glue( &e )) {
+			rc = test_filter( op, &e, op->ors_filter );
+			if ( rc == LDAP_COMPARE_TRUE ) {
+				rs->sr_entry = &e;
+				rs->sr_attrs = op->ors_attrs;
+				rs->sr_flags = 0;
+				rc = send_search_entry( op, rs );
+				rs->sr_entry = NULL;
+			} else {
+				rc = 0;
+			}
+		}
+		attrs_free( e.e_attrs );
+		e.e_attrs = NULL;
+		op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
+		if ( rc ) break;
+	}
+leave:
+	if ( sf ) delete sf;
+	return rc;
+}
+
+extern "C"
+int ndb_back_search( Operation *op, SlapReply *rs )
+{
+	struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+	NdbTransaction *txn;
+	NdbIndexScanOperation *scan;
+	NdbScanFilter *sf = NULL;
+	Entry e = {0};
+	int rc, i, ocfilter, indexed;
+	struct berval matched;
+	NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
+	char dnBuf[2048], *ptr;
+	char idbuf[2*sizeof(ID)];
+	char ocbuf[NDB_OC_BUFLEN];
+	NdbRdns rdns;
+	NdbOcInfo *oci;
+	NdbArgs NA;
+	slap_mask_t mask;
+	time_t stoptime;
+	int manageDSAit;
+
+	rc = ndb_thread_handle( op, &NA.ndb );
+	rdns.nr_num = 0;
+
+	manageDSAit = get_manageDSAit( op );
+
+	txn = NA.ndb->startTransaction();
+	if ( !txn ) {
+		Debug( LDAP_DEBUG_TRACE,
+			LDAP_XSTRING(ndb_back_search) ": startTransaction failed: %s (%d)\n",
+			NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+		rs->sr_err = LDAP_OTHER;
+		rs->sr_text = "internal error";
+		goto leave;
+	}
+
+	NA.txn = txn;
+	e.e_name = op->o_req_dn;
+	e.e_nname = op->o_req_ndn;
+	NA.e = &e;
+	NA.rdns = &rdns;
+	NA.ocs = NULL;
+
+	rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
+	if ( rs->sr_err ) {
+		if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+			rs->sr_matched = matched.bv_val;
+			if ( NA.ocs )
+				ndb_check_referral( op, rs, &NA );
+		}
+		goto leave;
+	}
+
+	if ( !access_allowed_mask( op, &e, slap_schema.si_ad_entry,
+		NULL, ACL_SEARCH, NULL, &mask )) {
+		if ( !ACL_GRANT( mask, ACL_DISCLOSE ))
+			rs->sr_err = LDAP_NO_SUCH_OBJECT;
+		else
+			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+		ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+		goto leave;
+	}
+
+	rs->sr_err = ndb_entry_get_data( op, &NA, 0 );
+	ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+	if ( rs->sr_err )
+		goto leave;
+
+	if ( !manageDSAit && is_entry_referral( &e )) {
+		rs->sr_ref = get_entry_referrals( op, &e );
+		rs->sr_err = LDAP_REFERRAL;
+		if ( rs->sr_ref )
+			rs->sr_flags |= REP_REF_MUSTBEFREED;
+		rs->sr_matched = e.e_name.bv_val;
+		attrs_free( e.e_attrs );
+		e.e_attrs = NULL;
+		goto leave;
+	}
+
+	if ( !manageDSAit && is_entry_glue( &e )) {
+		rs->sr_err = LDAP_NO_SUCH_OBJECT;
+		goto leave;
+	}
+
+	if ( get_assert( op ) && test_filter( op, &e, (Filter *)get_assertion( op )) !=
+		LDAP_COMPARE_TRUE ) {
+		rs->sr_err = LDAP_ASSERTION_FAILED;
+		attrs_free( e.e_attrs );
+		e.e_attrs = NULL;
+		goto leave;
+	}
+
+	/* admin ignores tlimits */
+	stoptime = op->o_time + op->ors_tlimit;
+
+	if ( op->ors_scope == LDAP_SCOPE_BASE ) {
+		rc = test_filter( op, &e, op->ors_filter );
+		if ( rc == LDAP_COMPARE_TRUE ) {
+			rs->sr_entry = &e;
+			rs->sr_attrs = op->ors_attrs;
+			rs->sr_flags = 0;
+			send_search_entry( op, rs );
+			rs->sr_entry = NULL;
+		}
+		attrs_free( e.e_attrs );
+		e.e_attrs = NULL;
+		rs->sr_err = LDAP_SUCCESS;
+		goto leave;
+	} else {
+		attrs_free( e.e_attrs );
+		e.e_attrs = NULL;
+		if ( rdns.nr_num == NDB_MAX_RDNS ) {
+			if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ||
+				op->ors_scope == LDAP_SCOPE_CHILDREN )
+			rs->sr_err = LDAP_SUCCESS;
+			goto leave;
+		}
+	}
+
+	/* See if we can handle the filter. Filtering on objectClass is only done
+	 * in the DN2ID table scan. If all other filter terms reside in one table,
+	 * then we scan the OC table instead of the DN2ID table.
+	 */
+	oci = NULL;
+	indexed = 0;
+	ocfilter = 0;
+	rc = ndb_filter_check( ni, op->ors_filter, &oci, &indexed, &ocfilter );
+	if ( rc ) {
+		Debug( LDAP_DEBUG_TRACE, "ndb_back_search: "
+			"filter attributes from multiple tables, indexing ignored\n",
+			0, 0, 0 );
+	} else if ( oci ) {
+		rc = ndb_oc_search( op, rs, NA.ndb, txn, &rdns, oci, indexed );
+		goto leave;
+	}
+
+	scan = txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
+	scan->readTuples( NdbOperation::LM_CommittedRead );
+	rc = ndb_dn2bound( scan, &rdns );
+
+	/* TODO: if ( ocfilter ) set up scanfilter for objectclass matches
+	 * column COND_LIKE "% <class> %"
+	 */
+
+	switch( op->ors_scope ) {
+	case LDAP_SCOPE_ONELEVEL:
+		sf = new NdbScanFilter(scan);
+		if ( sf->begin() < 0 ||
+			sf->cmp(NdbScanFilter::COND_NOT_LIKE, rc+3, "_%",
+				STRLENOF("_%")) < 0 ||
+			sf->end() < 0 ) {
+			rs->sr_err = LDAP_OTHER;
+			goto leave;
+		}
+		/* FALLTHRU */
+	case LDAP_SCOPE_CHILDREN:
+		/* Note: RDN_COLUMN offset not needed here */
+		scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
+		/* FALLTHRU */
+	case LDAP_SCOPE_SUBTREE:
+		break;
+	}
+	scanID = scan->getValue( EID_COLUMN, idbuf );
+	scanOC = scan->getValue( OCS_COLUMN, ocbuf );
+	for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+		rdns.nr_buf[i][0] = '\0';
+		scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
+	}
+	if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
+		rs->sr_err = LDAP_OTHER;
+		goto leave;
+	}
+
+	e.e_name.bv_val = dnBuf;
+	while ( scan->nextResult( true, true ) == 0 ) {
+		if ( op->o_abandon ) {
+			rs->sr_err = SLAPD_ABANDON;
+			break;
+		}
+		if ( slapd_shutdown ) {
+			rs->sr_err = LDAP_UNAVAILABLE;
+			break;
+		}
+		if ( op->ors_tlimit != SLAP_NO_LIMIT &&
+			slap_get_time() > stoptime ) {
+			rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+			break;
+		}
+		e.e_id = scanID->u_64_value();
+		NA.ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
+		for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+			if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
+				break;
+		}
+		ptr = dnBuf;
+		rdns.nr_num = i;
+		for ( --i; i>=0; i-- ) {
+			char *buf;
+			int len;
+			buf = rdns.nr_buf[i];
+			len = *buf++;
+			ptr = lutil_strncopy( ptr, buf, len );
+			if ( i ) *ptr++ = ',';
+		}
+		*ptr = '\0';
+		e.e_name.bv_len = ptr - dnBuf;
+		dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
+		NA.txn = NA.ndb->startTransaction();
+		rc = ndb_entry_get_data( op, &NA, 0 );
+		NA.txn->close();
+		ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+		if ( !manageDSAit && is_entry_referral( &e )) {
+			BerVarray erefs = get_entry_referrals( op, &e );
+			rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
+				op->ors_scope == LDAP_SCOPE_ONELEVEL ?
+					LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
+			rc = send_search_reference( op, rs );
+			ber_bvarray_free( rs->sr_ref );
+			ber_bvarray_free( erefs );
+			rs->sr_ref = NULL;
+		} else if ( manageDSAit || !is_entry_glue( &e )) {
+			rc = test_filter( op, &e, op->ors_filter );
+			if ( rc == LDAP_COMPARE_TRUE ) {
+				rs->sr_entry = &e;
+				rs->sr_attrs = op->ors_attrs;
+				rs->sr_flags = 0;
+				rc = send_search_entry( op, rs );
+				rs->sr_entry = NULL;
+			} else {
+				rc = 0;
+			}
+		}
+		attrs_free( e.e_attrs );
+		e.e_attrs = NULL;
+		op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
+		if ( rc ) break;
+	}
+leave:
+	if ( sf )
+		delete sf;
+	if ( txn )
+		txn->close();
+	send_ldap_result( op, rs );
+	return rs->sr_err;
+}
+
+extern NdbInterpretedCode *ndb_lastrow_code;	/* init.cpp */
+
+extern "C" int
+ndb_has_children(
+	NdbArgs *NA,
+	int *hasChildren
+)
+{
+	NdbIndexScanOperation *scan;
+	char idbuf[2*sizeof(ID)];
+	int rc;
+
+	if ( NA->rdns->nr_num >= NDB_MAX_RDNS ) {
+		*hasChildren = LDAP_COMPARE_FALSE;
+		return 0;
+	}
+
+	scan = NA->txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
+	if ( !scan )
+		return LDAP_OTHER;
+	scan->readTuples( NdbOperation::LM_Read, 0U, 0U, 1U );
+	rc = ndb_dn2bound( scan, NA->rdns );
+	if ( rc < NDB_MAX_RDNS ) {
+		scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
+	}
+#if 0
+	scan->interpret_exit_last_row();
+#else
+	scan->setInterpretedCode(ndb_lastrow_code);
+#endif
+	scan->getValue( EID_COLUMN, idbuf );
+	if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1 )) {
+		return LDAP_OTHER;
+	}
+	if (rc < NDB_MAX_RDNS && scan->nextResult( true, true ) == 0 )
+		*hasChildren = LDAP_COMPARE_TRUE;
+	else
+		*hasChildren = LDAP_COMPARE_FALSE;
+	scan->close();
+	return 0;
+}
+
+extern "C" int
+ndb_has_subordinates(
+	Operation *op,
+	Entry *e,
+	int *hasSubordinates )
+{
+	NdbArgs NA;
+	NdbRdns rdns;
+	int rc;
+
+	NA.rdns = &rdns;
+	rc = ndb_dn2rdns( &e->e_nname, &rdns );
+
+	if ( rc == 0 ) {
+		rc = ndb_thread_handle( op, &NA.ndb );
+		NA.txn = NA.ndb->startTransaction();
+		if ( NA.txn ) {
+			rc = ndb_has_children( &NA, hasSubordinates );
+			NA.txn->close();
+		}
+	}
+
+	return rc;
+}
+
+/*
+ * sets the supported operational attributes (if required)
+ */
+extern "C" int
+ndb_operational(
+	Operation	*op,
+	SlapReply	*rs )
+{
+	Attribute	**ap;
+
+	assert( rs->sr_entry != NULL );
+
+	for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next )
+		/* just count */ ;
+
+	if ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
+			ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) )
+	{
+		int	hasSubordinates, rc;
+
+		rc = ndb_has_subordinates( op, rs->sr_entry, &hasSubordinates );
+		if ( rc == LDAP_SUCCESS ) {
+			*ap = slap_operational_hasSubordinate( hasSubordinates == LDAP_COMPARE_TRUE );
+			assert( *ap != NULL );
+
+			ap = &(*ap)->a_next;
+		}
+	}
+
+	return LDAP_SUCCESS;
+}
+
diff --git a/servers/slapd/back-ndb/tools.cpp b/servers/slapd/back-ndb/tools.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5454175f5e50a1f8e662ac6044ecb991b99a8113
--- /dev/null
+++ b/servers/slapd/back-ndb/tools.cpp
@@ -0,0 +1,544 @@
+/* tools.cpp - tools for slap tools */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+#include "lutil.h"
+
+#include "back-ndb.h"
+
+typedef struct dn_id {
+	ID id;
+	struct berval dn;
+} dn_id;
+
+#define	HOLE_SIZE	4096
+static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
+static unsigned nhmax = HOLE_SIZE;
+static unsigned nholes;
+static Avlnode *myParents;
+
+static Ndb *myNdb;
+static NdbTransaction *myScanTxn;
+static NdbIndexScanOperation *myScanOp;
+
+static NdbRecAttr *myScanID, *myScanOC;
+static NdbRecAttr *myScanDN[NDB_MAX_RDNS];
+static char myDNbuf[2048];
+static char myIdbuf[2*sizeof(ID)];
+static char myOcbuf[NDB_OC_BUFLEN];
+static NdbRdns myRdns;
+
+static NdbTransaction *myPutTxn;
+static int myPutCnt;
+
+static struct berval *myOcList;
+static struct berval myDn;
+
+extern "C"
+int ndb_tool_entry_open(
+	BackendDB *be, int mode )
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+
+	myNdb = new Ndb( ni->ni_cluster[0], ni->ni_dbname );
+	return myNdb->init(1024);
+}
+
+extern "C"
+int ndb_tool_entry_close(
+	BackendDB *be )
+{
+	if ( myPutTxn ) {
+		int rc = myPutTxn->execute(NdbTransaction::Commit);
+		if( rc != 0 ) {
+			char text[1024];
+			snprintf( text, sizeof(text),
+					"txn_commit failed: %s (%d)",
+					myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
+			Debug( LDAP_DEBUG_ANY,
+				"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+				text, 0, 0 );
+		}
+		myPutTxn->close();
+		myPutTxn = NULL;
+	}
+	myPutCnt = 0;
+
+	if( nholes ) {
+		unsigned i;
+		fprintf( stderr, "Error, entries missing!\n");
+		for (i=0; i<nholes; i++) {
+			fprintf(stderr, "  entry %ld: %s\n",
+				holes[i].id, holes[i].dn.bv_val);
+		}
+		return -1;
+	}
+
+	return 0;
+}
+
+extern "C"
+ID ndb_tool_entry_next(
+	BackendDB *be )
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+	char *ptr;
+	ID id;
+	int i;
+
+	assert( be != NULL );
+	assert( slapMode & SLAP_TOOL_MODE );
+
+	if ( myScanOp->nextResult() ) {
+		myScanOp->close();
+		myScanOp = NULL;
+		myScanTxn->close();
+		myScanTxn = NULL;
+		return NOID;
+	}
+	id = myScanID->u_64_value();
+
+	if ( myOcList ) {
+		ber_bvarray_free( myOcList );
+	}
+	myOcList = ndb_ref2oclist( myOcbuf, NULL );
+	for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+		if ( myScanDN[i]->isNULL() || !myRdns.nr_buf[i][0] )
+			break;
+	}
+	myRdns.nr_num = i;
+	ptr = myDNbuf;
+	for ( --i; i>=0; i-- ) {
+		char *buf;
+		int len;
+		buf = myRdns.nr_buf[i];
+		len = *buf++;
+		ptr = lutil_strncopy( ptr, buf, len );
+		if ( i )
+			*ptr++ = ',';
+	}
+	*ptr = '\0';
+	myDn.bv_val = myDNbuf;
+	myDn.bv_len = ptr - myDNbuf;
+
+	return id;
+}
+
+extern "C"
+ID ndb_tool_entry_first(
+	BackendDB *be )
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+	int i;
+
+	myScanTxn = myNdb->startTransaction();
+	if ( !myScanTxn )
+		return NOID;
+
+	myScanOp = myScanTxn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
+	if ( !myScanOp )
+		return NOID;
+
+	if ( myScanOp->readTuples( NdbOperation::LM_CommittedRead, NdbScanOperation::SF_KeyInfo ))
+		return NOID;
+
+	myScanID = myScanOp->getValue( EID_COLUMN, myIdbuf );
+	myScanOC = myScanOp->getValue( OCS_COLUMN, myOcbuf );
+	for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+		myScanDN[i] = myScanOp->getValue( i+RDN_COLUMN, myRdns.nr_buf[i] );
+	}
+	if ( myScanTxn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 ))
+		return NOID;
+
+	return ndb_tool_entry_next( be );
+}
+
+extern "C"
+ID ndb_tool_dn2id_get(
+	Backend *be,
+	struct berval *dn
+)
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+	NdbArgs NA;
+	NdbRdns rdns;
+	Entry e;
+	char text[1024];
+	Operation op = {0};
+	Opheader ohdr = {0};
+	int rc;
+
+	if ( BER_BVISEMPTY(dn) )
+		return 0;
+
+	NA.ndb = myNdb;
+	NA.txn = myNdb->startTransaction();
+	if ( !NA.txn ) {
+		snprintf( text, sizeof(text),
+			"startTransaction failed: %s (%d)",
+			myNdb->getNdbError().message, myNdb->getNdbError().code );
+		Debug( LDAP_DEBUG_ANY,
+			"=> " LDAP_XSTRING(ndb_tool_dn2id_get) ": %s\n",
+			 text, 0, 0 );
+		return NOID;
+	}
+	if ( myOcList ) {
+		ber_bvarray_free( myOcList );
+		myOcList = NULL;
+	}
+	op.o_hdr = &ohdr;
+	op.o_bd = be;
+	op.o_tmpmemctx = NULL;
+	op.o_tmpmfuncs = &ch_mfuncs;
+
+	NA.e = &e;
+	e.e_name = *dn;
+	NA.rdns = &rdns;
+	NA.ocs = NULL;
+	rc = ndb_entry_get_info( &op, &NA, 0, NULL );
+	myOcList = NA.ocs;
+	NA.txn->close();
+	if ( rc )
+		return NOID;
+	
+	myDn = *dn;
+
+	return e.e_id;
+}
+
+extern "C"
+Entry* ndb_tool_entry_get( BackendDB *be, ID id )
+{
+	NdbArgs NA;
+	int rc;
+	char text[1024];
+	Operation op = {0};
+	Opheader ohdr = {0};
+
+	assert( be != NULL );
+	assert( slapMode & SLAP_TOOL_MODE );
+
+	NA.txn = myNdb->startTransaction();
+	if ( !NA.txn ) {
+		snprintf( text, sizeof(text),
+			"start_transaction failed: %s (%d)",
+			myNdb->getNdbError().message, myNdb->getNdbError().code );
+		Debug( LDAP_DEBUG_ANY,
+			"=> " LDAP_XSTRING(ndb_tool_entry_get) ": %s\n",
+			 text, 0, 0 );
+		return NULL;
+	}
+
+	NA.e = entry_alloc();
+	NA.e->e_id = id;
+	ber_dupbv( &NA.e->e_name, &myDn );
+	dnNormalize( 0, NULL, NULL, &NA.e->e_name, &NA.e->e_nname, NULL );
+
+	op.o_hdr = &ohdr;
+	op.o_bd = be;
+	op.o_tmpmemctx = NULL;
+	op.o_tmpmfuncs = &ch_mfuncs;
+
+	NA.ndb = myNdb;
+	NA.ocs = myOcList;
+	rc = ndb_entry_get_data( &op, &NA, 0 );
+
+	if ( rc ) {
+		entry_free( NA.e );
+		NA.e = NULL;
+	}
+	NA.txn->close();
+
+	return NA.e;
+}
+
+static struct berval glueval[] = {
+	BER_BVC("glue"),
+	BER_BVNULL
+};
+
+static int ndb_dnid_cmp( const void *v1, const void *v2 )
+{
+	struct dn_id *dn1 = (struct dn_id *)v1,
+		*dn2 = (struct dn_id *)v2;
+	return ber_bvcmp( &dn1->dn, &dn2->dn );
+}
+
+static int ndb_tool_next_id(
+	Operation *op,
+	NdbArgs *NA,
+	struct berval *text,
+	int hole )
+{
+	struct berval ndn = NA->e->e_nname;
+	int rc;
+
+	if (ndn.bv_len == 0) {
+		NA->e->e_id = 0;
+		return 0;
+	}
+
+	rc = ndb_entry_get_info( op, NA, 0, NULL );
+	if ( rc ) {
+		Attribute *a, tmp = {0};
+		if ( !be_issuffix( op->o_bd, &ndn ) ) {
+			struct dn_id *dptr;
+			struct berval npdn;
+			dnParent( &ndn, &npdn );
+			NA->e->e_nname = npdn;
+			NA->rdns->nr_num--;
+			rc = ndb_tool_next_id( op, NA, text, 1 );
+			NA->e->e_nname = ndn;
+			NA->rdns->nr_num++;
+			if ( rc ) {
+				return rc;
+			}
+			/* If parent didn't exist, it was created just now
+			 * and its ID is now in e->e_id.
+			 */
+			dptr = (struct dn_id *)ch_malloc( sizeof( struct dn_id ) + npdn.bv_len + 1);
+			dptr->id = NA->e->e_id;
+			dptr->dn.bv_val = (char *)(dptr+1);
+			strcpy(dptr->dn.bv_val, npdn.bv_val );
+			dptr->dn.bv_len = npdn.bv_len;
+			if ( avl_insert( &myParents, dptr, ndb_dnid_cmp, avl_dup_error )) {
+				ch_free( dptr );
+			}
+		}
+		rc = ndb_next_id( op->o_bd, myNdb, &NA->e->e_id );
+		if ( rc ) {
+			snprintf( text->bv_val, text->bv_len,
+				"next_id failed: %s (%d)",
+				myNdb->getNdbError().message, myNdb->getNdbError().code );
+			Debug( LDAP_DEBUG_ANY,
+				"=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
+			return rc;
+		}
+		if ( hole ) {
+			a = NA->e->e_attrs;
+			NA->e->e_attrs = &tmp;
+			tmp.a_desc = slap_schema.si_ad_objectClass;
+			tmp.a_vals = glueval;
+			tmp.a_nvals = tmp.a_vals;
+			tmp.a_numvals = 1;
+		}
+		rc = ndb_entry_put_info( op->o_bd, NA, 0 );
+		if ( hole ) {
+			NA->e->e_attrs = a;
+		}
+		if ( rc ) {
+			snprintf( text->bv_val, text->bv_len, 
+				"ndb_entry_put_info failed: %s (%d)",
+				myNdb->getNdbError().message, myNdb->getNdbError().code );
+		Debug( LDAP_DEBUG_ANY,
+			"=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
+		} else if ( hole ) {
+			if ( nholes == nhmax - 1 ) {
+				if ( holes == hbuf ) {
+					holes = (dn_id *)ch_malloc( nhmax * sizeof(dn_id) * 2 );
+					AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
+				} else {
+					holes = (dn_id *)ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
+				}
+				nhmax *= 2;
+			}
+			ber_dupbv( &holes[nholes].dn, &ndn );
+			holes[nholes++].id = NA->e->e_id;
+		}
+	} else if ( !hole ) {
+		unsigned i;
+
+		for ( i=0; i<nholes; i++) {
+			if ( holes[i].id == NA->e->e_id ) {
+				int j;
+				free(holes[i].dn.bv_val);
+				for (j=i;j<nholes;j++) holes[j] = holes[j+1];
+				holes[j].id = 0;
+				nholes--;
+				rc = ndb_entry_put_info( op->o_bd, NA, 1 );
+				break;
+			} else if ( holes[i].id > NA->e->e_id ) {
+				break;
+			}
+		}
+	}
+	return rc;
+}
+
+extern "C"
+ID ndb_tool_entry_put(
+	BackendDB *be,
+	Entry *e,
+	struct berval *text )
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+	struct dn_id dtmp, *dptr;
+	NdbArgs NA;
+	NdbRdns rdns;
+	int rc, slow = 0;
+	Operation op = {0};
+	Opheader ohdr = {0};
+
+	assert( be != NULL );
+	assert( slapMode & SLAP_TOOL_MODE );
+
+	assert( text != NULL );
+	assert( text->bv_val != NULL );
+	assert( text->bv_val[0] == '\0' );	/* overconservative? */
+
+	Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(ndb_tool_entry_put)
+		"( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
+
+	if ( !be_issuffix( be, &e->e_nname )) {
+		dnParent( &e->e_nname, &dtmp.dn );
+		dptr = (struct dn_id *)avl_find( myParents, &dtmp, ndb_dnid_cmp );
+		if ( !dptr )
+			slow = 1;
+	}
+
+	rdns.nr_num = 0;
+
+	op.o_hdr = &ohdr;
+	op.o_bd = be;
+	op.o_tmpmemctx = NULL;
+	op.o_tmpmfuncs = &ch_mfuncs;
+
+	if ( !slow ) {
+		rc = ndb_next_id( be, myNdb, &e->e_id );
+		if ( rc ) {
+			snprintf( text->bv_val, text->bv_len,
+				"next_id failed: %s (%d)",
+				myNdb->getNdbError().message, myNdb->getNdbError().code );
+			Debug( LDAP_DEBUG_ANY,
+				"=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
+			return rc;
+		}
+	}
+
+	if ( !myPutTxn )
+		myPutTxn = myNdb->startTransaction();
+	if ( !myPutTxn ) {
+		snprintf( text->bv_val, text->bv_len,
+			"start_transaction failed: %s (%d)",
+			myNdb->getNdbError().message, myNdb->getNdbError().code );
+		Debug( LDAP_DEBUG_ANY,
+			"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+			 text->bv_val, 0, 0 );
+		return NOID;
+	}
+
+	/* add dn2id indices */
+	ndb_dn2rdns( &e->e_name, &rdns );
+	NA.rdns = &rdns;
+	NA.e = e;
+	NA.ndb = myNdb;
+	NA.txn = myPutTxn;
+	if ( slow ) {
+		rc = ndb_tool_next_id( &op, &NA, text, 0 );
+		if( rc != 0 ) {
+			goto done;
+		}
+	} else {
+		rc = ndb_entry_put_info( be, &NA, 0 );
+		if ( rc != 0 ) {
+			goto done;
+		}
+	}
+
+	/* id2entry index */
+	rc = ndb_entry_put_data( be, &NA );
+	if( rc != 0 ) {
+		snprintf( text->bv_val, text->bv_len,
+				"ndb_entry_put_data failed: %s (%d)",
+				myNdb->getNdbError().message, myNdb->getNdbError().code );
+		Debug( LDAP_DEBUG_ANY,
+			"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+			text->bv_val, 0, 0 );
+		goto done;
+	}
+
+done:
+	if( rc == 0 ) {
+		myPutCnt++;
+		if ( !( myPutCnt & 0x0f )) {
+			rc = myPutTxn->execute(NdbTransaction::Commit);
+			if( rc != 0 ) {
+				snprintf( text->bv_val, text->bv_len,
+					"txn_commit failed: %s (%d)",
+					myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
+				Debug( LDAP_DEBUG_ANY,
+					"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+					text->bv_val, 0, 0 );
+				e->e_id = NOID;
+			}
+			myPutTxn->close();
+			myPutTxn = NULL;
+		}
+	} else {
+		snprintf( text->bv_val, text->bv_len,
+			"txn_aborted! %s (%d)",
+			myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
+		Debug( LDAP_DEBUG_ANY,
+			"=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+			text->bv_val, 0, 0 );
+		e->e_id = NOID;
+		myPutTxn->close();
+	}
+
+	return e->e_id;
+}
+
+extern "C"
+int ndb_tool_entry_reindex(
+	BackendDB *be,
+	ID id,
+	AttributeDescription **adv )
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+
+	Debug( LDAP_DEBUG_ARGS,
+		"=> " LDAP_XSTRING(ndb_tool_entry_reindex) "( %ld )\n",
+		(long) id, 0, 0 );
+
+	return 0;
+}
+
+extern "C"
+ID ndb_tool_entry_modify(
+	BackendDB *be,
+	Entry *e,
+	struct berval *text )
+{
+	struct ndb_info *ni = (struct ndb_info *) be->be_private;
+	int rc;
+
+	Debug( LDAP_DEBUG_TRACE,
+		"=> " LDAP_XSTRING(ndb_tool_entry_modify) "( %ld, \"%s\" )\n",
+		(long) e->e_id, e->e_dn, 0 );
+
+done:
+	return e->e_id;
+}
+
diff --git a/tests/data/ndb.conf b/tests/data/ndb.conf
new file mode 100644
index 0000000000000000000000000000000000000000..e4f90b5e02fe7f9e69243963214c1caaed4e1aa9
--- /dev/null
+++ b/tests/data/ndb.conf
@@ -0,0 +1,23 @@
+# back-ndb boilerplate config -- for testing
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2008 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+
+dbuser	root
+dbhost	localhost
+dbconnect	127.0.0.1
+dbsocket	/tmp/mysql.sock
+attrset extensibleObject uidNumber,gidNumber
+attrblob description
+index cn
+#index sn
diff --git a/tests/data/slapd-2db.conf b/tests/data/slapd-2db.conf
index 92a5fbebd9e9748c62b7c502cdec94c169b43f07..46a1536802a1a72f145610697b5de0d4cb743dd1 100644
--- a/tests/data/slapd-2db.conf
+++ b/tests/data/slapd-2db.conf
@@ -38,6 +38,8 @@ subordinate
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 database	@BACKEND@
 suffix		"dc=example,dc=com"
@@ -48,5 +50,7 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-aci.conf b/tests/data/slapd-aci.conf
index cb471a6e52fba91f31f6c001ec949cf39071e1fe..3abae9d1b9e495549a55e5fdb5a0e33453d6f706 100644
--- a/tests/data/slapd-aci.conf
+++ b/tests/data/slapd-aci.conf
@@ -50,6 +50,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 access to dn.subtree="dc=example,dc=com"
 	by dynacl/aci write
diff --git a/tests/data/slapd-acl.conf b/tests/data/slapd-acl.conf
index 225f81330512dbe6630f639e9f0f585c267665cb..bfcabfc491e925741006cb83619712432f62bd0a 100644
--- a/tests/data/slapd-acl.conf
+++ b/tests/data/slapd-acl.conf
@@ -50,6 +50,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 #access		to attrs=objectclass dn.subtree="dc=example,dc=com"
 access		to attrs=objectclass
diff --git a/tests/data/slapd-cache-master.conf b/tests/data/slapd-cache-master.conf
index b9bb86fcc0e0a6d335f320154e1cbb97a4e246f4..9ebff79d4e64e001424252313abd25c47896d9fd 100644
--- a/tests/data/slapd-cache-master.conf
+++ b/tests/data/slapd-cache-master.conf
@@ -40,5 +40,7 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-chain1.conf b/tests/data/slapd-chain1.conf
index ed7de05b59737311312ee69c01694cb61bcf1242..7e416bc2debb77f948b7fc73e7dfa0e87a86343b 100644
--- a/tests/data/slapd-chain1.conf
+++ b/tests/data/slapd-chain1.conf
@@ -58,5 +58,7 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-chain2.conf b/tests/data/slapd-chain2.conf
index b51dc112b9e4bffd27aa5621828a43c564f3760a..5769b0e02639601ea3da1aa5e61d13cb7f3d7216 100644
--- a/tests/data/slapd-chain2.conf
+++ b/tests/data/slapd-chain2.conf
@@ -47,6 +47,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 #
 # uses the chain overlay as database specific;
diff --git a/tests/data/slapd-component.conf b/tests/data/slapd-component.conf
index ce0ec3cd85106be787ff511e3db2cd760d8689da..b2553f2a6d1766feb152f9ef1d8e7af1498445b3 100644
--- a/tests/data/slapd-component.conf
+++ b/tests/data/slapd-component.conf
@@ -43,5 +43,7 @@ rootdn		"cn=Manager,dc=example,dc=com"
 rootpw		secret
 #bdb#index		objectClass eq
 #hdb#index		objectClass eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-config-undo.conf b/tests/data/slapd-config-undo.conf
index 5b1e51ab3cba839151d01fad185af405e216cd31..edf0087b058fb35f6bef0fd5eb07e2b3794f29f3 100644
--- a/tests/data/slapd-config-undo.conf
+++ b/tests/data/slapd-config-undo.conf
@@ -14,6 +14,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
 
diff --git a/tests/data/slapd-dds.conf b/tests/data/slapd-dds.conf
index abd558d80b9b468ae1408c491e8d37ad9bc258a8..eacc2ec8ad800a52fccac3fc094bc73873652dd9 100644
--- a/tests/data/slapd-dds.conf
+++ b/tests/data/slapd-dds.conf
@@ -46,6 +46,8 @@ rootpw		secret
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		entryExpireTimestamp	eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 overlay		dds
 dds-max-ttl	1d
diff --git a/tests/data/slapd-deltasync-master.conf b/tests/data/slapd-deltasync-master.conf
index 4d4412138bd6df3aba48494f0a9642fa9ecdfd79..cc99050890e0ee79435cbd0bdc4bfec0664ac908 100644
--- a/tests/data/slapd-deltasync-master.conf
+++ b/tests/data/slapd-deltasync-master.conf
@@ -43,6 +43,8 @@ rootdn		"cn=Manager,dc=example,dc=com"
 #bdb#index		entryUUID,entryCSN	eq
 #hdb#index		objectClass	eq
 #hdb#index		entryUUID,entryCSN	eq
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 overlay syncprov
 syncprov-reloadhint true
@@ -60,6 +62,8 @@ rootpw		secret
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		entryUUID,entryCSN	eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 
 access to *
diff --git a/tests/data/slapd-deltasync-slave.conf b/tests/data/slapd-deltasync-slave.conf
index 1b0309d5cef334070958129d87cbf03dd464e58a..81fe48010e72f635b679e48ec8474d3c73adfaa4 100644
--- a/tests/data/slapd-deltasync-slave.conf
+++ b/tests/data/slapd-deltasync-slave.conf
@@ -51,6 +51,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_3
+#ndb#include @DATADIR@/ndb.conf
 
 # Don't change syncrepl spec yet
 syncrepl	rid=1
diff --git a/tests/data/slapd-dn.conf b/tests/data/slapd-dn.conf
index e02641693a0ff9f78ebb04ea925017568cf5b2ae..05838d692afccf6264155f8d3e01783738065aea 100644
--- a/tests/data/slapd-dn.conf
+++ b/tests/data/slapd-dn.conf
@@ -42,5 +42,7 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-dynlist.conf b/tests/data/slapd-dynlist.conf
index 64498fd15a7176cc40e7b5103b800e92abdfa6a3..196ae7b11096b1442975f9bca5a85b5602582a8f 100644
--- a/tests/data/slapd-dynlist.conf
+++ b/tests/data/slapd-dynlist.conf
@@ -46,6 +46,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 # we'll reconfigure the attrset dynamically
 overlay			dynlist
diff --git a/tests/data/slapd-emptydn.conf b/tests/data/slapd-emptydn.conf
index f5eb23c45e77db6493269ff0f45b1a9d03b7dc35..99a3fd1b7b4d412fbddd5c21cb2d3b8d10f7d92a 100644
--- a/tests/data/slapd-emptydn.conf
+++ b/tests/data/slapd-emptydn.conf
@@ -47,6 +47,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 access		to attrs=userPassword
 		by dn.exact="cn=Manager,c=US" write
@@ -70,6 +72,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 access		to attrs=userPassword
 		by self =wx
diff --git a/tests/data/slapd-glue-syncrepl1.conf b/tests/data/slapd-glue-syncrepl1.conf
index 55cbc3551480df6c0f5648ca014e8bea52cdefdc..67fa4acf8b1f833b834a6f85fced04c5ae4a710f 100644
--- a/tests/data/slapd-glue-syncrepl1.conf
+++ b/tests/data/slapd-glue-syncrepl1.conf
@@ -44,6 +44,8 @@ rootdn		"cn=Manager 1,dc=example,dc=com"
 #hdb#index		uid	pres,eq,sub
 #hdb#index		cn,sn	pres,eq,sub,subany
 #hdb#index		entryUUID,entryCSN	pres
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 overlay		syncprov
 
@@ -60,6 +62,8 @@ rootdn		"cn=Manager 1,dc=example,dc=com"
 #hdb#index		uid	pres,eq,sub
 #hdb#index		cn,sn	pres,eq,sub,subany
 #hdb#index		entryUUID,entryCSN	pres
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 syncrepl	rid=1
 		provider=@URI2@
@@ -88,6 +92,8 @@ rootpw		secret
 #hdb#index		objectclass	eq
 #hdb#index		uid	pres,eq,sub
 #hdb#index		cn,sn	pres,eq,sub,subany
+#ndb#dbname db_3
+#ndb#include @DATADIR@/ndb.conf
 
 #overlay		syncprov
 
diff --git a/tests/data/slapd-glue-syncrepl2.conf b/tests/data/slapd-glue-syncrepl2.conf
index 1f148835885bbd41c859aa499c5b1938d09883ae..6965d0330249787caa0a0ff0c7d9fa76b2497a24 100644
--- a/tests/data/slapd-glue-syncrepl2.conf
+++ b/tests/data/slapd-glue-syncrepl2.conf
@@ -44,6 +44,8 @@ rootdn		"cn=Manager 2,dc=example,dc=com"
 #hdb#index		uid	pres,eq,sub
 #hdb#index		cn,sn	pres,eq,sub,subany
 #hdb#index		entryUUID,entryCSN	pres
+#ndb#dbname db_4
+#ndb#include @DATADIR@/ndb.conf
 
 
 syncrepl	rid=2
@@ -74,6 +76,8 @@ rootdn		"cn=Manager 2,dc=example,dc=com"
 #hdb#index		uid	pres,eq,sub
 #hdb#index		cn,sn	pres,eq,sub,subany
 #hdb#index		entryUUID,entryCSN	pres
+#ndb#dbname db_5
+#ndb#include @DATADIR@/ndb.conf
 
 
 overlay		syncprov
@@ -90,6 +94,8 @@ rootpw		secret
 #hdb#index		objectclass	eq
 #hdb#index		uid	pres,eq,sub
 #hdb#index		cn,sn	pres,eq,sub,subany
+#ndb#dbname db_6
+#ndb#include @DATADIR@/ndb.conf
 
 
 #overlay		syncprov
diff --git a/tests/data/slapd-glue.conf b/tests/data/slapd-glue.conf
index 22c5f9f4c789747132244532a09f4b3f45c3ca37..b56693d57ad83e05e4641f85d2857c927b1d3fb4 100644
--- a/tests/data/slapd-glue.conf
+++ b/tests/data/slapd-glue.conf
@@ -41,6 +41,8 @@ rootdn		"cn=Manager, dc=example,dc=com"
 #hdb#index		objectclass	eq
 #hdb#index		uid	pres,eq,sub
 #hdb#index		cn,sn	pres,eq,sub,subany
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 database	@BACKEND@
 suffix		"ou=Groups,dc=example,dc=com"
@@ -53,6 +55,8 @@ rootdn		"cn=Manager, dc=example,dc=com"
 #hdb#index		objectclass	eq
 #hdb#index		uid	pres,eq,sub
 #hdb#index		cn,sn	pres,eq,sub,subany
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 database	@BACKEND@
 suffix		"dc=example,dc=com"
@@ -65,5 +69,7 @@ rootpw		secret
 #hdb#index		objectclass	eq
 #hdb#index		uid	pres,eq,sub
 #hdb#index		cn,sn	pres,eq,sub,subany
+#ndb#dbname db_3
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-idassert.conf b/tests/data/slapd-idassert.conf
index f7a37ddc2f1c977b018958145374016f037ce7fa..4fae1f8eaa8c84669fd727c910ab68d3280f941b 100644
--- a/tests/data/slapd-idassert.conf
+++ b/tests/data/slapd-idassert.conf
@@ -66,6 +66,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 access to dn.exact="cn=Proxy,ou=Admin,dc=example,dc=com"
 		attrs=authzTo
@@ -82,6 +84,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 database	ldap
 suffix		"o=Example,c=US"
diff --git a/tests/data/slapd-ldapglue.conf b/tests/data/slapd-ldapglue.conf
index 83dfcd72ced886f11dc403999df871b5e3fe0ddf..1102a91423e1f58c7ea06f27d6f018385944889b 100644
--- a/tests/data/slapd-ldapglue.conf
+++ b/tests/data/slapd-ldapglue.conf
@@ -75,5 +75,7 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-ldapgluegroups.conf b/tests/data/slapd-ldapgluegroups.conf
index 510ada4b6473b9713f4ffcd4f3de979f89d7eede..2e26bb8f9b47e01583cf7e3405b3f13fa17bb1d4 100644
--- a/tests/data/slapd-ldapgluegroups.conf
+++ b/tests/data/slapd-ldapgluegroups.conf
@@ -57,5 +57,7 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_6
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-ldapgluepeople.conf b/tests/data/slapd-ldapgluepeople.conf
index 76d7fecc26afae300bedfe1b61644c873e31c4c1..653926b0a19ab192f0e8e27fc77f575ffb5a9296 100644
--- a/tests/data/slapd-ldapgluepeople.conf
+++ b/tests/data/slapd-ldapgluepeople.conf
@@ -58,5 +58,7 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_5
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-limits.conf b/tests/data/slapd-limits.conf
index be5e082fd47887bc7bf73b08dede4cdac76d2431..0abb921369e8f7dc2ef537565a2f6112684b218a 100644
--- a/tests/data/slapd-limits.conf
+++ b/tests/data/slapd-limits.conf
@@ -41,6 +41,8 @@ rootpw		secret
 #bdb#index		uid eq
 #hdb#index		objectClass eq
 #hdb#index		uid eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 # Need extra limits for pagedResults on backends that support it...
 #bdb#limits	dn.exact="cn=Unlimited User,ou=Paged Results Users,dc=example,dc=com" size=4 size.pr=unlimited
diff --git a/tests/data/slapd-master.conf b/tests/data/slapd-master.conf
index 938c1829092acd06e3b30555d6cd65a40d965972..a02ed5dad8cd1345c04e5f43f51a1bf26f7e8d9d 100644
--- a/tests/data/slapd-master.conf
+++ b/tests/data/slapd-master.conf
@@ -40,5 +40,7 @@ rootdn		"cn=Manager,dc=example,dc=com"
 rootpw		secret
 #bdb#index		objectClass eq
 #hdb#index		objectClass eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-meta-target1.conf b/tests/data/slapd-meta-target1.conf
index e77e4a9d2ab8f85a6445881a6fc6ed3dd9f749d9..7c0de78cf0d68e733a5fb52779b0f6c4df2357d8 100644
--- a/tests/data/slapd-meta-target1.conf
+++ b/tests/data/slapd-meta-target1.conf
@@ -45,6 +45,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 # ITS#5154: force mixed success/failure of binds using same connection
 access to dn="cn=Barbara Jensen,ou=Information Technology DivisioN,ou=People,dc=example,dc=com"
diff --git a/tests/data/slapd-meta-target2.conf b/tests/data/slapd-meta-target2.conf
index 493d3ffb214fc57c69651a7d31c898bb14da0b6f..e7e5f62ea55fe2a2bbb10393853485d7f1f4ec04 100644
--- a/tests/data/slapd-meta-target2.conf
+++ b/tests/data/slapd-meta-target2.conf
@@ -48,5 +48,7 @@ rootdn		"cn=Manager,ou=Meta,dc=example,dc=com"
 rootpw		secret
 #bdb#index		objectClass eq
 #hdb#index		objectClass eq
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-ppolicy.conf b/tests/data/slapd-ppolicy.conf
index 6ee15bf21e99f53c275a42f31386b68e7588b2a5..9e2ddcb267f45600e89372c4414cdf9514bd1dc7 100644
--- a/tests/data/slapd-ppolicy.conf
+++ b/tests/data/slapd-ppolicy.conf
@@ -40,6 +40,8 @@ rootdn		"cn=Manager,dc=example,dc=com"
 rootpw		secret
 #bdb#index		objectClass eq
 #hdb#index		objectClass eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 overlay		ppolicy
 ppolicy_default	"cn=Standard Policy,ou=Policies,dc=example,dc=com"
diff --git a/tests/data/slapd-proxycache.conf b/tests/data/slapd-proxycache.conf
index c8e5405c700eca38e35028323aa66140d3d599b0..195e774c4fc472f139077e768487d766fb446b20 100644
--- a/tests/data/slapd-proxycache.conf
+++ b/tests/data/slapd-proxycache.conf
@@ -60,5 +60,7 @@ directory	@TESTDIR@/db.2.a
 #bdb#index		cn,sn,uid,mail	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid,mail	pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-pw.conf b/tests/data/slapd-pw.conf
index ce58f2d2453280aeb5ae29009174a1fff2a3a326..1a0e93f6822a14589f8d141b5c9a81ac35f38225 100644
--- a/tests/data/slapd-pw.conf
+++ b/tests/data/slapd-pw.conf
@@ -40,6 +40,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 #
 # normal installations should protect root dse,
diff --git a/tests/data/slapd-ref-slave.conf b/tests/data/slapd-ref-slave.conf
index 41ced9e1e8b53575a1157ae689913fecd0e5cdc8..620c37b78ca85104ecca3e962178911fb6ec5f46 100644
--- a/tests/data/slapd-ref-slave.conf
+++ b/tests/data/slapd-ref-slave.conf
@@ -45,5 +45,7 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-referrals.conf b/tests/data/slapd-referrals.conf
index c2442eea01b93ea21f1c80d76174c6bb402e483a..e337f05632c1207915ddbbc73d9590b63b77e0b3 100644
--- a/tests/data/slapd-referrals.conf
+++ b/tests/data/slapd-referrals.conf
@@ -37,5 +37,7 @@ rootdn		"cn=Manager,c=us"
 rootpw		secret
 #bdb#index		objectClass eq
 #hdb#index		objectClass eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-refint.conf b/tests/data/slapd-refint.conf
index 5b83b29b929a9f200906343c381e0181bd9b84fa..4eac2cbfe6fa1012927de681bd7a49577e2e5175 100644
--- a/tests/data/slapd-refint.conf
+++ b/tests/data/slapd-refint.conf
@@ -43,6 +43,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 overlay		refint
 refint_attributes	manager secretary member
diff --git a/tests/data/slapd-relay.conf b/tests/data/slapd-relay.conf
index b40bce96e1acc5f2a30577bd2230e280c6f43e93..add6d5fab4fce22634709d3f5058f2ed9e11bafe 100644
--- a/tests/data/slapd-relay.conf
+++ b/tests/data/slapd-relay.conf
@@ -46,6 +46,8 @@ rootdn		"cn=Manager,dc=example,dc=com"
 rootpw		secret
 #bdb#index		objectClass eq
 #hdb#index		objectClass eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 database	@RELAY@
 suffix		"o=Example,c=US"
diff --git a/tests/data/slapd-repl-slave-remote.conf b/tests/data/slapd-repl-slave-remote.conf
index dcb220cb026cdc8283f368b34af6298c2c3102c4..ccba5e83ed1cd4a02e7a33323c45c54a645eb7d1 100644
--- a/tests/data/slapd-repl-slave-remote.conf
+++ b/tests/data/slapd-repl-slave-remote.conf
@@ -63,6 +63,8 @@ updateref	@URI1@
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		entryUUID	pres,eq
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 # Need to strip hasSubordinates from internal searches otherwise
 # syncrepl will try to delete it, since syncprov is not sending
diff --git a/tests/data/slapd-retcode.conf b/tests/data/slapd-retcode.conf
index 968b065c6d2e16207ae67f8a5b5326489360d929..5493598ea5912e29d97ae78242fcb637a23ebdc8 100644
--- a/tests/data/slapd-retcode.conf
+++ b/tests/data/slapd-retcode.conf
@@ -44,6 +44,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 overlay		retcode
 retcode-parent	"ou=RetCodes,dc=example,dc=com"
diff --git a/tests/data/slapd-schema.conf b/tests/data/slapd-schema.conf
index 182c9c758cf36453ff9fca0e3ca7c89919909244..d6a23357ad60d92608e3ad5ba464a19f8c8d945e 100644
--- a/tests/data/slapd-schema.conf
+++ b/tests/data/slapd-schema.conf
@@ -49,11 +49,7 @@ suffix		"o=OpenLDAP Project,l=Internet"
 directory	@TESTDIR@/db.1.a
 #bdb#index		objectClass eq
 #hdb#index		objectClass eq
-
-#database	@BACKEND@
-#suffix		"dc=example,dc=com"
-#directory	@TESTDIR@/db.1.b
-##bdb#index		objectClass eq
-##hdb#index		objectClass eq
+#ndb#dbname db_1_a
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd-syncrepl-master.conf b/tests/data/slapd-syncrepl-master.conf
index d436d66407c1789ed2d107d07edfc166d83e5d0d..1dc83e4b1f64ad7699fad8a38d05c0e320ff9c46 100644
--- a/tests/data/slapd-syncrepl-master.conf
+++ b/tests/data/slapd-syncrepl-master.conf
@@ -44,6 +44,8 @@ rootpw		secret
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		entryUUID,entryCSN	eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 overlay	syncprov
 #syncprov-sessionlog 100
diff --git a/tests/data/slapd-syncrepl-multiproxy.conf b/tests/data/slapd-syncrepl-multiproxy.conf
index 2a6a1f9c0a77f758aed0ae72d6391723aab89ea6..ce79cf436015e4c1c2f2dada8ab4979489612e46 100644
--- a/tests/data/slapd-syncrepl-multiproxy.conf
+++ b/tests/data/slapd-syncrepl-multiproxy.conf
@@ -46,6 +46,8 @@ rootpw		secret
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		entryUUID,entryCSN	eq
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 overlay	syncprov
 syncprov-sessionlog 100
diff --git a/tests/data/slapd-syncrepl-slave-persist1.conf b/tests/data/slapd-syncrepl-slave-persist1.conf
index b8c2fa98f3541514f7202eb16154f89a2ba57f8c..dc3f9b6195b0ffee1ab69970cd84ae67203bc62f 100644
--- a/tests/data/slapd-syncrepl-slave-persist1.conf
+++ b/tests/data/slapd-syncrepl-slave-persist1.conf
@@ -51,6 +51,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_4
+#ndb#include @DATADIR@/ndb.conf
 
 # Don't change syncrepl spec yet
 syncrepl	rid=1
diff --git a/tests/data/slapd-syncrepl-slave-persist2.conf b/tests/data/slapd-syncrepl-slave-persist2.conf
index e4a949f2779154661b0eeb9f9987e3b0dde1f6ff..6f9617362eeeada8fe6c31693163d1d9f3571920 100644
--- a/tests/data/slapd-syncrepl-slave-persist2.conf
+++ b/tests/data/slapd-syncrepl-slave-persist2.conf
@@ -30,6 +30,8 @@ rootpw		secret
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		entryUUID,entryCSN	eq
+#ndb#dbname db_5
+#ndb#include @DATADIR@/ndb.conf
 
 # Don't change syncrepl spec yet
 syncrepl	rid=1
diff --git a/tests/data/slapd-syncrepl-slave-persist3.conf b/tests/data/slapd-syncrepl-slave-persist3.conf
index 014083808e936d270451ff6960aaccf3583a62fe..201aaeb750af474f9eec1e0c6c3e971f4105f382 100644
--- a/tests/data/slapd-syncrepl-slave-persist3.conf
+++ b/tests/data/slapd-syncrepl-slave-persist3.conf
@@ -42,6 +42,8 @@ rootpw		secret
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		entryUUID,entryCSN	eq
+#ndb#dbname db_6
+#ndb#include @DATADIR@/ndb.conf
 
 # Don't change syncrepl spec yet
 syncrepl	rid=1
diff --git a/tests/data/slapd-syncrepl-slave-refresh1.conf b/tests/data/slapd-syncrepl-slave-refresh1.conf
index 133f4721d5eb57dca427657100bc72cbe802ca58..1a83a0d39379b08ed7effe14f9e2b61af3c492e8 100644
--- a/tests/data/slapd-syncrepl-slave-refresh1.conf
+++ b/tests/data/slapd-syncrepl-slave-refresh1.conf
@@ -44,6 +44,8 @@ rootpw		secret
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		entryUUID,entryCSN	eq
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 # Don't change syncrepl spec yet
 syncrepl	rid=1
diff --git a/tests/data/slapd-syncrepl-slave-refresh2.conf b/tests/data/slapd-syncrepl-slave-refresh2.conf
index 90f64e2a41e3725a7097e67c63c490c9bb4cb7db..fccae2ef7bf2c5f8f96ac4c456ac66e2f41330dc 100644
--- a/tests/data/slapd-syncrepl-slave-refresh2.conf
+++ b/tests/data/slapd-syncrepl-slave-refresh2.conf
@@ -42,6 +42,8 @@ rootpw		secret
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		entryUUID,entryCSN	eq
+#ndb#dbname db_3
+#ndb#include @DATADIR@/ndb.conf
 
 # Don't change syncrepl spec yet
 syncrepl	rid=1
diff --git a/tests/data/slapd-translucent-local.conf b/tests/data/slapd-translucent-local.conf
index 54c5ffb5c5ea16594f121a7bc9fe345c1d1e152f..85da7e748634a52b3f0d8b79a337820a904d2bf5 100644
--- a/tests/data/slapd-translucent-local.conf
+++ b/tests/data/slapd-translucent-local.conf
@@ -48,6 +48,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 overlay		translucent
 translucent_no_glue
diff --git a/tests/data/slapd-translucent-remote.conf b/tests/data/slapd-translucent-remote.conf
index 5123437b34c89f088d64bed657d23bd46859fe92..3f10b6aa1f72af44df0aa013812280f35f9736f1 100644
--- a/tests/data/slapd-translucent-remote.conf
+++ b/tests/data/slapd-translucent-remote.conf
@@ -13,7 +13,6 @@
 ## top-level directory of the distribution or, alternatively, at
 ## <http://www.OpenLDAP.org/license.html>.
 
-ucdata-path	./ucdata
 include		@SCHEMADIR@/core.schema
 include		@SCHEMADIR@/cosine.schema
 include		@SCHEMADIR@/inetorgperson.schema
@@ -44,3 +43,5 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
diff --git a/tests/data/slapd-unique.conf b/tests/data/slapd-unique.conf
index 45f04155c59440cf4055a4843e8f7a241ef093bd..0184828eb0c80be21a8d75af69ce2f990b466a9f 100644
--- a/tests/data/slapd-unique.conf
+++ b/tests/data/slapd-unique.conf
@@ -43,6 +43,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 overlay			unique
 
diff --git a/tests/data/slapd-valsort.conf b/tests/data/slapd-valsort.conf
index a9414cfe5ecbc39594ed3efda507e69d65812830..b42bb10bc4db0e6c0d54263df79263466e7008fa 100644
--- a/tests/data/slapd-valsort.conf
+++ b/tests/data/slapd-valsort.conf
@@ -41,6 +41,8 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 overlay			valsort
 valsort-attr		sn ou=users,o=valsort alpha-ascend
diff --git a/tests/data/slapd-whoami.conf b/tests/data/slapd-whoami.conf
index 405b0a5d6218ea094296e4e73f46c01291c78141..e2cfe816b954bfb5dd02b5efced24e23ed83a03a 100644
--- a/tests/data/slapd-whoami.conf
+++ b/tests/data/slapd-whoami.conf
@@ -60,5 +60,7 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd.conf b/tests/data/slapd.conf
index 399ccc506e4c8a028debce09c17523a06897f577..f53d0e8f142c7009642e493f5997119be6762a37 100644
--- a/tests/data/slapd.conf
+++ b/tests/data/slapd.conf
@@ -47,5 +47,7 @@ rootpw		secret
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
 #hdb#checkpoint		1 1
+#ndb#dbname db_1
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/data/slapd2.conf b/tests/data/slapd2.conf
index e2db4974982f530c973f04075154a59dd6a66417..7d09fa5b8cb877a838b61d426434f782ef3020ca 100644
--- a/tests/data/slapd2.conf
+++ b/tests/data/slapd2.conf
@@ -40,5 +40,7 @@ rootpw		secret
 #bdb#index		cn,sn,uid	pres,eq,sub
 #hdb#index		objectClass	eq
 #hdb#index		cn,sn,uid	pres,eq,sub
+#ndb#dbname db_2
+#ndb#include @DATADIR@/ndb.conf
 
 #monitor#database	monitor
diff --git a/tests/run.in b/tests/run.in
index 6330069c8ee481af2132ce9f7007687561967c7c..e74a31083498c9906d1508d41a32defa5a13f850 100644
--- a/tests/run.in
+++ b/tests/run.in
@@ -177,6 +177,16 @@ if test -d ${TESTDIR} ; then
 		/bin/rm -rf ${TESTDIR}/db.*
 	fi
 fi
+if test $BACKEND = ndb ; then
+	mysql --user root <<EOF
+	drop database if exists db_1;
+	drop database if exists db_2;
+	drop database if exists db_3;
+	drop database if exists db_4;
+	drop database if exists db_5;
+	drop database if exists db_6;
+EOF
+fi
 mkdir -p ${TESTDIR}
 
 if test $USERDATA = yes ; then
diff --git a/tests/scripts/acfilter.sh b/tests/scripts/acfilter.sh
index 9df3765ec0a55f94f50936fdc40f62a6d141ed5f..00ea8933175e4184b5278f18f8d58d9deb7c832b 100755
--- a/tests/scripts/acfilter.sh
+++ b/tests/scripts/acfilter.sh
@@ -13,6 +13,10 @@
 ## top-level directory of the distribution or, alternatively, at
 ## <http://www.OpenLDAP.org/license.html>.
 #
-# Strip comments
+# Strip comments, sort attributes. Requires GNU awk
 #
+if [ "$BACKEND" != ndb ]; then
 grep -v '^#'
+else
+grep -v '^#'| awk 'BEGIN{FS="\n";RS=""} {j=0; for (i=1; i<=NF; i++){ if ($i ~ /^ /){ x[j] = x[j] "\n" $i; } else { j++; x[j] = $i } } print x[1]; delete x[1]; j=asort(x); for (i=1; i<=j; i++){ print x[i]; } delete x; print "" }'
+fi
diff --git a/tests/scripts/all b/tests/scripts/all
index e08b6939db4e574408441d02cec741810151d731..c8be543e61f2ed316da41f49e12643277c3cb1d4 100755
--- a/tests/scripts/all
+++ b/tests/scripts/all
@@ -32,6 +32,16 @@ for CMD in $SRCDIR/scripts/test*; do
 	else
 		/bin/rm -rf $TESTDIR
 	fi
+	if test $BACKEND = ndb ; then
+		mysql --user root <<EOF
+		drop database if exists db_1;
+		drop database if exists db_2;
+		drop database if exists db_3;
+		drop database if exists db_4;
+		drop database if exists db_5;
+		drop database if exists db_6;
+EOF
+	fi
 
 	echo ">>>>> Starting ${TB}`basename $CMD`${TN} ..."
 	$CMD