]> git.sur5r.net Git - openldap/commitdiff
ITS#8114 OpenLDAP WiredTiger Backend
authorHAMANO Tsukasa <hamano@osstech.co.jp>
Mon, 27 Apr 2015 01:27:58 +0000 (10:27 +0900)
committerHoward Chu <hyc@openldap.org>
Wed, 19 Aug 2015 17:13:27 +0000 (18:13 +0100)
26 files changed:
build/top.mk
configure.in
doc/man/man5/slapd-wt.5 [new file with mode: 0644]
servers/slapd/back-wt/Makefile.in [new file with mode: 0644]
servers/slapd/back-wt/add.c [new file with mode: 0644]
servers/slapd/back-wt/attr.c [new file with mode: 0644]
servers/slapd/back-wt/back-wt.h [new file with mode: 0644]
servers/slapd/back-wt/bind.c [new file with mode: 0644]
servers/slapd/back-wt/compare.c [new file with mode: 0644]
servers/slapd/back-wt/config.c [new file with mode: 0644]
servers/slapd/back-wt/ctx.c [new file with mode: 0644]
servers/slapd/back-wt/delete.c [new file with mode: 0644]
servers/slapd/back-wt/dn2entry.c [new file with mode: 0644]
servers/slapd/back-wt/dn2id.c [new file with mode: 0644]
servers/slapd/back-wt/filterindex.c [new file with mode: 0644]
servers/slapd/back-wt/id2entry.c [new file with mode: 0644]
servers/slapd/back-wt/idl.c [new file with mode: 0644]
servers/slapd/back-wt/idl.h [new file with mode: 0644]
servers/slapd/back-wt/index.c [new file with mode: 0644]
servers/slapd/back-wt/init.c [new file with mode: 0644]
servers/slapd/back-wt/key.c [new file with mode: 0644]
servers/slapd/back-wt/nextid.c [new file with mode: 0644]
servers/slapd/back-wt/operational.c [new file with mode: 0644]
servers/slapd/back-wt/proto-wt.h [new file with mode: 0644]
servers/slapd/back-wt/search.c [new file with mode: 0644]
servers/slapd/back-wt/tools.c [new file with mode: 0644]

index d6a751b0c5c57cf2e650a131c313ede34b9cf9d4..3ac38ec0b4f04d862ade756a8dff855355d95db9 100644 (file)
@@ -161,6 +161,7 @@ LTHREAD_LIBS = @LTHREAD_LIBS@
 
 BDB_LIBS = @BDB_LIBS@
 SLAPD_NDB_LIBS = @SLAPD_NDB_LIBS@
+WT_LIBS = @WT_LIBS@
 
 LDAP_LIBLBER_LA = $(LDAP_LIBDIR)/liblber/liblber.la
 LDAP_LIBLDAP_LA = $(LDAP_LIBDIR)/libldap/libldap.la
index 72cd4c0631fb2e0f29b8d66eb4786d97441f5249..cc95802472e3cf381df48a85fb5d15b7ff1ecd98 100644 (file)
@@ -296,7 +296,8 @@ Backends="bdb \
        relay \
        shell \
        sock \
-       sql"
+       sql \
+       wt"
 
 AC_ARG_ENABLE(xxslapbackends,[
 SLAPD Backend Options:])
@@ -333,6 +334,8 @@ OL_ARG_ENABLE(sock,[    --enable-sock         enable sock backend],
        no, [no yes mod], ol_enable_backends)dnl
 OL_ARG_ENABLE(sql,[    --enable-sql      enable sql backend],
        no, [no yes mod], ol_enable_backends)dnl
+OL_ARG_ENABLE(wt,[    --enable-wt        enable WiredTiger backend],
+       no, [no yes mod], ol_enable_backends)dnl
 
 dnl ----------------------------------------------------------------
 dnl SLAPD Overlay Options
@@ -485,7 +488,8 @@ elif test $ol_enable_modules != yes &&
        test $ol_enable_relay = no &&
        test $ol_enable_shell = no &&
        test $ol_enable_sock = no &&
-       test $ol_enable_sql = no ; then
+       test $ol_enable_sql = no &&
+       test $ol_enable_wt = no ; then
        dnl no slapd backend
 
        if test $ol_enable_slapd = yes ; then
@@ -548,6 +552,7 @@ BUILD_RELAY=no
 BUILD_SHELL=no
 BUILD_SOCK=no
 BUILD_SQL=no
+BUILD_WT=no
 
 BUILD_ACCESSLOG=no
 BUILD_AUDITLOG=no
@@ -2082,6 +2087,33 @@ if test $ol_enable_ndb != no ; then
        fi
 fi
 
+dnl ----------------------------------------------------------------
+dnl WiredTiger
+ol_link_wt=no
+if test $ol_enable_wt != no ; then
+       AC_CHECK_PROG(PKGCONFIG,pkg-config,yes)
+       if test "$PKGCONFIG" != yes ; then
+               AC_MSG_ERROR([could not locate pkg-config])
+       fi
+       WT_INCS=`pkg-config --cflags wiredtiger`
+       WT_LIBS=`pkg-config --libs wiredtiger`
+
+       save_CFLAGS="$CFLAGS"
+       save_LDFLAGS="$LDFLAGS"
+       CFLAGS="$WT_INCS"
+       CPPFLAGS="$WT_INCS"
+       LDFLAGS="$WT_LIBS"
+       AC_CHECK_HEADERS([wiredtiger.h])
+       AC_CHECK_LIB(wiredtiger,wiredtiger_version,[: ok],[
+               AC_MSG_ERROR([could not locate wiredtiger library])
+       ])
+       CFLAGS="$save_CFLAGS"
+       CPPFLAGS="$save_CPPFLAGS"
+       LDFLAGS="$save_LDFLAGS"
+       SLAPD_LIBS="$SLAPD_LIBS \$(WT_LIBS)"
+       ol_link_wt=yes
+fi
+
 dnl ----------------------------------------------------------------
 dnl International Components for Unicode
 OL_ICU
@@ -2811,6 +2843,19 @@ if test "$ol_link_sql" != no ; then
        AC_DEFINE_UNQUOTED(SLAPD_SQL,$MFLAG,[define to support SQL backend])
 fi
 
+if test "$ol_link_wt" != no ; then
+       BUILD_SLAPD=yes
+       BUILD_WT=$ol_enable_wt
+       if test "$ol_enable_wt" = mod; then
+               SLAPD_DYNAMIC_BACKENDS="$SLAPD_DYNAMIC_BACKENDS back-wt"
+               MFLAG=SLAPD_MOD_DYNAMIC
+       else
+               SLAPD_STATIC_BACKENDS="$SLAPD_STATIC_BACKENDS back-wt"
+               MFLAG=SLAPD_MOD_STATIC
+       fi
+       AC_DEFINE_UNQUOTED(SLAPD_WT,$MFLAG,[define to support WiredTiger backend])
+fi
+
 if test "$ol_enable_accesslog" != no ; then
        BUILD_ACCESSLOG=$ol_enable_accesslog
        if test "$ol_enable_accesslog" = mod ; then
@@ -3106,6 +3151,7 @@ dnl backends
   AC_SUBST(BUILD_SHELL)
   AC_SUBST(BUILD_SOCK)
   AC_SUBST(BUILD_SQL)
+  AC_SUBST(BUILD_WT)
 dnl overlays
   AC_SUBST(BUILD_ACCESSLOG)
   AC_SUBST(BUILD_AUDITLOG)
@@ -3169,6 +3215,9 @@ AC_SUBST(SLAPD_SQL_LDFLAGS)
 AC_SUBST(SLAPD_SQL_LIBS)
 AC_SUBST(SLAPD_SQL_INCLUDES)
 
+AC_SUBST(WT_INCS)
+AC_SUBST(WT_LIBS)
+
 dnl ----------------------------------------------------------------
 dnl final help output
 AC_ARG_WITH(xxinstall,[
@@ -3213,6 +3262,7 @@ AC_CONFIG_FILES([Makefile:build/top.mk:Makefile.in:build/dir.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/back-wt/Makefile:build/top.mk:servers/slapd/back-wt/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]
diff --git a/doc/man/man5/slapd-wt.5 b/doc/man/man5/slapd-wt.5
new file mode 100644 (file)
index 0000000..072e09b
--- /dev/null
@@ -0,0 +1,90 @@
+.TH SLAPD-WT 5 "RELEASEDATE" "OpenLDAP LDVERSION"
+.\" Copyright 2011-2015 The OpenLDAP Foundation All Rights Reserved.
+.\" Copying restrictions apply.  See COPYRIGHT/LICENSE.
+.\" $OpenLDAP$
+.SH NAME
+slapd\-wt \- WiredTiger backend to slapd
+.SH SYNOPSIS
+.B ETCDIR/slapd.conf
+.SH DESCRIPTION
+The \fBwt\fP backend to
+.BR slapd (8)
+uses WiredTiger database library to store data.
+.LP
+The \fBwt\fP backend is experimental module that have potential high
+write performance and high concurrency performance.
+This backend have not some basic feature yet. Please backup data using
+slapcat before update the module.
+
+.SH CONFIGURATION
+These
+.B slapd.conf
+options apply to the \fBwt\fP backend database.
+That is, they must follow a "database wt" line and
+come before any subsequent "backend" or "database" lines.
+Other database options are described in the
+.BR slapd.conf (5)
+manual page.
+.TP
+.BI directory \ <directory>
+Specify WiredTiger home directory that containing this database and
+associated indexes live.
+A separate directory must be specified for each database.
+The default is
+.BR LOCALSTATEDIR/openldap\-data .
+.TP
+\fBwtconfig \fR{\fBcreate\fR,\fBcache_size=512M\fR,\fBasync=(enabled)\fR}
+Specify configuration for wiredtiger, This parameter is pass to
+.BR wiredtiger_open (3).
+.RS
+.TP
+.B create
+create the database if it does not exist.
+.RE
+.RS
+.TP
+.B cache_size
+maximum heap memory to allocate for the cache.
+.RE
+.RS
+.TP
+.B async
+asynchronous operations configuration options. disabled by default.
+.RE
+.RS
+.TP
+\fBindex \fR{\fI<attrlist>\fR|\fBdefault\fR} [\fBpres\fR,\fBeq\fR,\fBapprox\fR,\fBsub\fR,\fI<special>\fR]
+Specify the indexes to maintain for the given attribute (or
+list of attributes).
+Some attributes only support a subset of indexes.
+If only an \fI<attr>\fP is given, the indices specified for \fBdefault\fR
+are maintained.
+Note that setting a default does not imply that all attributes will be
+indexed. Also, for best performance, an
+.B eq
+index should always be configured for the
+.B objectClass
+attribute.
+
+.SH ACCESS CONTROL
+The
+.B wt
+backend honors 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\-config (5),
+.BR slapd (8),
+.BR slapadd (8),
+.BR slapcat (8),
+.BR slapindex (8),
+WiredTiger documentation.
+.SH ACKNOWLEDGEMENTS
+.so ../Project
+Written by HAMANO Tsukasa <hamano@osstech.co.jp>.
diff --git a/servers/slapd/back-wt/Makefile.in b/servers/slapd/back-wt/Makefile.in
new file mode 100644 (file)
index 0000000..e167645
--- /dev/null
@@ -0,0 +1,54 @@
+# Makefile.in for back-wt
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 1998-2015 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>.
+
+SRCS = init.c tools.c config.c \
+       add.c bind.c compare.c delete.c search.c \
+       operational.c \
+       attr.c index.c key.c filterindex.c \
+       dn2entry.c dn2id.c id2entry.c idl.c \
+       nextid.c ctx.c
+
+OBJS = init.lo tools.lo config.lo \
+       add.lo bind.lo compare.lo delete.lo search.lo \
+       operational.lo \
+       attr.lo index.lo key.lo filterindex.lo \
+       dn2entry.lo dn2id.lo id2entry.lo idl.lo \
+       nextid.lo ctx.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-wt"
+BUILD_MOD = @BUILD_WT@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = @WT_INCS@
+MOD_LIBS = @WT_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_wt
+
+XINCPATH = -I.. -I$(srcdir)/..
+XDEFS = $(MODULES_CPPFLAGS)
+
+all-local-lib: ../.backend
+
+../.backend: lib$(LIBBASE).a
+       @touch $@
+
diff --git a/servers/slapd/back-wt/add.c b/servers/slapd/back-wt/add.c
new file mode 100644 (file)
index 0000000..7dae9b1
--- /dev/null
@@ -0,0 +1,408 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "back-wt.h"
+#include "config.h"
+
+int
+wt_add( Operation *op, SlapReply *rs )
+{
+    struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       struct berval   pdn;
+       char textbuf[SLAP_TEXT_BUFLEN];
+       size_t textlen = sizeof textbuf;
+       AttributeDescription *children = slap_schema.si_ad_children;
+       AttributeDescription *entry = slap_schema.si_ad_entry;
+       ID eid;
+       int num_retries = 0;
+       int success;
+       LDAPControl **postread_ctrl = NULL;
+       LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+       int num_ctrls = 0;
+       wt_ctx *wc;
+       Entry *e = NULL;
+       Entry *p = NULL;
+       ID pid;
+       int rc;
+
+    Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(wt_add) ": %s\n",
+                  op->ora_e->e_name.bv_val, 0, 0);
+
+       ctrls[num_ctrls] = 0;
+
+       /* check entry's schema */
+       rs->sr_err = entry_schema_check(
+               op, op->ora_e, NULL,
+               get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
+    if ( rs->sr_err != LDAP_SUCCESS ) {
+        Debug( LDAP_DEBUG_TRACE,
+                          LDAP_XSTRING(wt_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(wt_add)
+                          ": entry failed op attrs add: %s (%d)\n",
+                          rs->sr_text, rs->sr_err, 0 );
+        goto return_results;
+    }
+
+    if ( get_assert( op ) &&
+                ( test_filter( op, op->ora_e, get_assertion( op ))
+                  != LDAP_COMPARE_TRUE ))
+    {
+        rs->sr_err = LDAP_ASSERTION_FAILED;
+        goto return_results;
+    }
+
+       /* Not used
+        * subentry = is_entry_subentry( op->ora_e );
+        */
+
+    /*
+     * Get the parent dn and see if the corresponding entry exists.
+     */
+    if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
+        pdn = slap_empty_bv;
+    } else {
+        dnParent( &op->ora_e->e_nname, &pdn );
+    }
+
+       wc = wt_ctx_get(op, wi);
+       if( !wc ){
+        Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_add)
+                          ": wt_ctx_get failed\n",
+                          0, 0, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "internal error";
+        send_ldap_result( op, rs );
+        return rs->sr_err;
+       }
+
+       rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+       switch( rc ) {
+       case 0:
+               rs->sr_err = LDAP_ALREADY_EXISTS;
+               goto return_results;
+               break;
+       case WT_NOTFOUND:
+               break;
+       default:
+               /* TODO: retry handling */
+        Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_add)
+                          ": error at wt_dn2entry() rc=%d\n",
+                          rc, 0, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "internal error";
+               goto return_results;
+       }
+
+    /* get parent entry */
+       rc = wt_dn2pentry(op->o_bd, wc, &op->o_req_ndn, &p);
+       switch( rc ){
+       case 0:
+       case WT_NOTFOUND:
+               break;
+       default:
+        Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_add)
+                          ": error at wt_dn2pentry() rc=%d\n",
+                          rc, 0, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "internal error";
+               goto return_results;
+       }
+
+       if ( !p )
+               p = (Entry *)&slap_entry_root;
+
+       if ( !bvmatch( &pdn, &p->e_nname ) ) {
+               rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
+                                                                          op->o_tmpmemctx );
+               if ( p != (Entry *)&slap_entry_root ) {
+                       rs->sr_ref = is_entry_referral( p )
+                               ? get_entry_referrals( op, p )
+                               : NULL;
+                       wt_entry_return( p );
+               } else {
+                       rs->sr_ref = NULL;
+               }
+               p = NULL;
+        Debug( LDAP_DEBUG_TRACE,
+                          LDAP_XSTRING(wt_add) ": parent "
+                          "does not exist\n", 0, 0, 0 );
+        rs->sr_err = LDAP_REFERRAL;
+        rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+        goto return_results;
+       }
+
+       rs->sr_err = access_allowed( op, p,
+                                                                children, NULL, ACL_WADD, NULL );
+       if ( ! rs->sr_err ) {
+               /*
+               if ( p != (Entry *)&slap_entry_root )
+                       wt_entry_return( op, p );
+               */
+               p = NULL;
+
+               Debug( LDAP_DEBUG_TRACE,
+                          LDAP_XSTRING(wt_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 ( p != (Entry *)&slap_entry_root ) {
+               if ( is_entry_subentry( p ) ) {
+                       wt_entry_return( p );
+                       p = NULL;
+                       /* parent is a subentry, don't allow add */
+                       Debug( LDAP_DEBUG_TRACE,
+                                  LDAP_XSTRING(wt_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 ) ) {
+                       wt_entry_return( p );
+                       p = NULL;
+                       /* parent is an alias, don't allow add */
+                       Debug( LDAP_DEBUG_TRACE,
+                                  LDAP_XSTRING(wt_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 ) ) {
+                       BerVarray ref = get_entry_referrals( op, p );
+                       /* parent is a referral, don't allow add */
+                       rs->sr_matched = ber_strdup_x( p->e_name.bv_val,
+                                                                                  op->o_tmpmemctx );
+                       rs->sr_ref = referral_rewrite( ref, &p->e_name,
+                                                                                  &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+                       ber_bvarray_free( ref );
+                       wt_entry_return( p );
+                       p = NULL;
+                       Debug( LDAP_DEBUG_TRACE,
+                                  LDAP_XSTRING(wt_add) ": parent is referral\n",
+                                  0, 0, 0 );
+
+                       rs->sr_err = LDAP_REFERRAL;
+                       rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+                       goto return_results;
+               }
+       }
+
+#if 0
+       if ( subentry ) {
+               /* FIXME: */
+               /* parent must be an administrative point of the required kind */
+       }
+#endif
+
+       /* free parent */
+       if ( p != (Entry *)&slap_entry_root ) {
+               pid = p->e_id;
+               if ( p->e_nname.bv_len ) {
+                       struct berval ppdn;
+
+                       /* ITS#5326: use parent's DN if differs from provided one */
+                       dnParent( &op->ora_e->e_name, &ppdn );
+                       if ( !dn_match( &p->e_name, &ppdn ) ) {
+                               struct berval rdn;
+                               struct berval newdn;
+
+                               dnRdn( &op->ora_e->e_name, &rdn );
+
+                               build_new_dn( &newdn, &p->e_name, &rdn, NULL );
+                               if ( op->ora_e->e_name.bv_val != op->o_req_dn.bv_val )
+                                       ber_memfree( op->ora_e->e_name.bv_val );
+                               op->ora_e->e_name = newdn;
+
+                               /* FIXME: should check whether
+                 * dnNormalize(newdn) == e->e_nname ... */
+                       }
+               }
+
+               wt_entry_return( p );
+       }
+       p = NULL;
+
+       rs->sr_err = access_allowed( op, op->ora_e,
+                                                                entry, NULL, ACL_WADD, NULL );
+
+       if ( ! rs->sr_err ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          LDAP_XSTRING(wt_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;
+       }
+
+       /*
+        * Check ACL for attribute write access
+        */
+       if (!acl_check_modlist(op, op->ora_e, op->ora_modlist)) {
+               Debug( LDAP_DEBUG_TRACE,
+                          LDAP_XSTRING(wt_add) ": no write access to attribute\n",
+                          0, 0, 0 );
+               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+               rs->sr_text = "no write access to attribute";
+               goto return_results;
+       }
+
+       rc = wc->session->begin_transaction(wc->session, NULL);
+       if( rc ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          LDAP_XSTRING(wt_add) ": begin_transaction failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "begin_transaction failed";
+               goto return_results;
+       }
+       Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(wt_add) ": session id: %p\n",
+                  wc->session, 0, 0 );
+
+       wt_next_id( op->o_bd, &eid );
+       op->ora_e->e_id = eid;
+
+       rc = wt_dn2id_add( op, wc->session, pid, op->ora_e );
+       if( rc ){
+               Debug( LDAP_DEBUG_TRACE,
+                          LDAP_XSTRING(wt_add)
+                          ": dn2id_add failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               switch( rc ) {
+               case WT_DUPLICATE_KEY:
+                       rs->sr_err = LDAP_ALREADY_EXISTS;
+                       break;
+               default:
+                       rs->sr_err = LDAP_OTHER;
+               }
+               wc->session->rollback_transaction(wc->session, NULL);
+               goto return_results;
+       }
+
+       rc = wt_id2entry_add( op, wc->session, op->ora_e );
+       if ( rc ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          LDAP_XSTRING(wt_add)
+                          ": id2entry_add failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               if ( rc == LDAP_ADMINLIMIT_EXCEEDED ) {
+                       rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+                       rs->sr_text = "entry is too big";
+               } else {
+                       rs->sr_err = LDAP_OTHER;
+                       rs->sr_text = "entry store failed";
+               }
+               wc->session->rollback_transaction(wc->session, NULL);
+               goto return_results;
+       }
+
+       /* add indices */
+       rc = wt_index_entry_add( op, wc, op->ora_e );
+       if ( rc ) {
+               Debug(LDAP_DEBUG_TRACE,
+                         "<== " LDAP_XSTRING(wt_add)
+                         ": index add failed: %s (%d)\n",
+                         wiredtiger_strerror(rc), rc, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "index add failed";
+               wc->session->rollback_transaction(wc->session, NULL);
+               goto return_results;
+       }
+
+       rc = wc->session->commit_transaction(wc->session, NULL);
+       if( rc ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          "<== " LDAP_XSTRING(wt_add)
+                          ": commit_transaction failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "commit_transaction failed";
+               goto return_results;
+       }
+
+       rs->sr_err = LDAP_SUCCESS;
+
+       /* 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->ora_e,
+                                                                &slap_post_read_bv, postread_ctrl ) )
+               {
+                       Debug( LDAP_DEBUG_TRACE,
+                                  "<=- " LDAP_XSTRING(wt_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;
+                       }
+               }
+       }
+
+       Debug(LDAP_DEBUG_TRACE,
+                 LDAP_XSTRING(wt_add) ": added%s id=%08lx dn=\"%s\"\n",
+                 op->o_noop ? " (no-op)" : "",
+                 op->ora_e->e_id, op->ora_e->e_dn );
+
+return_results:
+       success = rs->sr_err;
+       send_ldap_result( op, rs );
+
+       slap_graduate_commit_csn( op );
+
+       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;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/attr.c b/servers/slapd/back-wt/attr.c
new file mode 100644 (file)
index 0000000..a681f21
--- /dev/null
@@ -0,0 +1,388 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "back-wt.h"
+#include "config.h"
+
+/* Find the ad, return -1 if not found,
+ * set point for insertion if ins is non-NULL
+ */
+int
+wt_attr_slot( struct wt_info *wi, AttributeDescription *ad, int *ins )
+{
+       unsigned base = 0, cursor = 0;
+       unsigned n = wi->wi_nattrs;
+       int val = 0;
+
+       while ( 0 < n ) {
+               unsigned pivot = n >> 1;
+               cursor = base + pivot;
+
+               val = SLAP_PTRCMP( ad, wi->wi_attrs[cursor]->ai_desc );
+               if ( val < 0 ) {
+                       n = pivot;
+               } else if ( val > 0 ) {
+                       base = cursor + 1;
+                       n -= pivot + 1;
+               } else {
+                       return cursor;
+               }
+       }
+       if ( ins ) {
+               if ( val > 0 )
+                       ++cursor;
+               *ins = cursor;
+       }
+       return -1;
+}
+
+static int
+ainfo_insert( struct wt_info *wi, AttrInfo *a )
+{
+       int x;
+       int i = wt_attr_slot( wi, a->ai_desc, &x );
+
+       /* Is it a dup? */
+       if ( i >= 0 )
+               return -1;
+
+       wi->wi_attrs = ch_realloc( wi->wi_attrs, ( wi->wi_nattrs+1 ) *
+                                                          sizeof( AttrInfo * ));
+       if ( x < wi->wi_nattrs )
+               AC_MEMCPY( &wi->wi_attrs[x+1], &wi->wi_attrs[x],
+                                  ( wi->wi_nattrs - x ) * sizeof( AttrInfo *));
+       wi->wi_attrs[x] = a;
+       wi->wi_nattrs++;
+       return 0;
+}
+
+AttrInfo *
+wt_attr_mask(
+       struct wt_info  *wi,
+       AttributeDescription *desc )
+{
+       int i = wt_attr_slot( wi, desc, NULL );
+       return i < 0 ? NULL : wi->wi_attrs[i];
+}
+
+int
+wt_attr_index_config(
+       struct wt_info  *wi,
+       const char              *fname,
+       int                     lineno,
+       int                     argc,
+       char            **argv,
+       struct          config_reply_s *c_reply)
+{
+       int rc = 0;
+       int     i;
+       slap_mask_t mask;
+       char **attrs;
+       char **indexes = NULL;
+
+       attrs = ldap_str2charray( argv[0], "," );
+
+       if( attrs == NULL ) {
+               fprintf( stderr, "%s: line %d: "
+                       "no attributes specified: %s\n",
+                       fname, lineno, argv[0] );
+               return LDAP_PARAM_ERROR;
+       }
+
+       if ( argc > 1 ) {
+               indexes = ldap_str2charray( argv[1], "," );
+
+               if( indexes == NULL ) {
+                       fprintf( stderr, "%s: line %d: "
+                               "no indexes specified: %s\n",
+                               fname, lineno, argv[1] );
+                       rc = LDAP_PARAM_ERROR;
+                       goto done;
+               }
+       }
+
+       if( indexes == NULL ) {
+               mask = wi->wi_defaultmask;
+
+       } else {
+               mask = 0;
+
+               for ( i = 0; indexes[i] != NULL; i++ ) {
+                       slap_mask_t index;
+
+                       rc = slap_str2index( indexes[i], &index );
+
+                       if( rc != LDAP_SUCCESS ) {
+                               if ( c_reply )
+                               {
+                                       snprintf(c_reply->msg, sizeof(c_reply->msg),
+                                               "index type \"%s\" undefined", indexes[i] );
+
+                                       fprintf( stderr, "%s: line %d: %s\n",
+                                               fname, lineno, c_reply->msg );
+                               }
+                               rc = LDAP_PARAM_ERROR;
+                               goto done;
+                       }
+
+                       mask |= index;
+               }
+       }
+
+       if( !mask ) {
+               if ( c_reply )
+               {
+                       snprintf(c_reply->msg, sizeof(c_reply->msg),
+                               "no indexes selected" );
+                       fprintf( stderr, "%s: line %d: %s\n",
+                               fname, lineno, c_reply->msg );
+               }
+               rc = LDAP_PARAM_ERROR;
+               goto done;
+       }
+
+       for ( i = 0; attrs[i] != NULL; i++ ) {
+               AttrInfo        *a;
+               AttributeDescription *ad;
+               const char *text;
+#ifdef LDAP_COMP_MATCH
+               ComponentReference* cr = NULL;
+               AttrInfo *a_cr = NULL;
+#endif
+
+               if( strcasecmp( attrs[i], "default" ) == 0 ) {
+                       wi->wi_defaultmask |= mask;
+                       continue;
+               }
+
+#ifdef LDAP_COMP_MATCH
+               if ( is_component_reference( attrs[i] ) ) {
+                       rc = extract_component_reference( attrs[i], &cr );
+                       if ( rc != LDAP_SUCCESS ) {
+                               if ( c_reply )
+                               {
+                                       snprintf(c_reply->msg, sizeof(c_reply->msg),
+                                               "index component reference\"%s\" undefined",
+                                               attrs[i] );
+                                       fprintf( stderr, "%s: line %d: %s\n",
+                                               fname, lineno, c_reply->msg );
+                               }
+                               goto done;
+                       }
+                       cr->cr_indexmask = mask;
+                       /*
+                        * After extracting a component reference
+                        * only the name of a attribute will be remaining
+                        */
+               } else {
+                       cr = NULL;
+               }
+#endif
+               ad = NULL;
+               rc = slap_str2ad( attrs[i], &ad, &text );
+
+               if( rc != LDAP_SUCCESS ) {
+                       if ( c_reply )
+                       {
+                               snprintf(c_reply->msg, sizeof(c_reply->msg),
+                                       "index attribute \"%s\" undefined",
+                                       attrs[i] );
+
+                               fprintf( stderr, "%s: line %d: %s\n",
+                                       fname, lineno, c_reply->msg );
+                       }
+fail:
+#ifdef LDAP_COMP_MATCH
+                       ch_free( cr );
+#endif
+                       goto done;
+               }
+
+               if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) {
+                       if (c_reply) {
+                               snprintf(c_reply->msg, sizeof(c_reply->msg),
+                                       "index of attribute \"%s\" disallowed", attrs[i] );
+                               fprintf( stderr, "%s: line %d: %s\n",
+                                       fname, lineno, c_reply->msg );
+                       }
+                       rc = LDAP_UNWILLING_TO_PERFORM;
+                       goto fail;
+               }
+
+               if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !(
+                       ad->ad_type->sat_approx
+                               && ad->ad_type->sat_approx->smr_indexer
+                               && ad->ad_type->sat_approx->smr_filter ) )
+               {
+                       if (c_reply) {
+                               snprintf(c_reply->msg, sizeof(c_reply->msg),
+                                       "approx index of attribute \"%s\" disallowed", attrs[i] );
+                               fprintf( stderr, "%s: line %d: %s\n",
+                                       fname, lineno, c_reply->msg );
+                       }
+                       rc = LDAP_INAPPROPRIATE_MATCHING;
+                       goto fail;
+               }
+
+               if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !(
+                       ad->ad_type->sat_equality
+                               && ad->ad_type->sat_equality->smr_indexer
+                               && ad->ad_type->sat_equality->smr_filter ) )
+               {
+                       if (c_reply) {
+                               snprintf(c_reply->msg, sizeof(c_reply->msg),
+                                       "equality index of attribute \"%s\" disallowed", attrs[i] );
+                               fprintf( stderr, "%s: line %d: %s\n",
+                                       fname, lineno, c_reply->msg );
+                       }
+                       rc = LDAP_INAPPROPRIATE_MATCHING;
+                       goto fail;
+               }
+
+               if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !(
+                       ad->ad_type->sat_substr
+                               && ad->ad_type->sat_substr->smr_indexer
+                               && ad->ad_type->sat_substr->smr_filter ) )
+               {
+                       if (c_reply) {
+                               snprintf(c_reply->msg, sizeof(c_reply->msg),
+                                       "substr index of attribute \"%s\" disallowed", attrs[i] );
+                               fprintf( stderr, "%s: line %d: %s\n",
+                                       fname, lineno, c_reply->msg );
+                       }
+                       rc = LDAP_INAPPROPRIATE_MATCHING;
+                       goto fail;
+               }
+
+               Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
+                       ad->ad_cname.bv_val, mask, 0 );
+
+               a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
+
+#ifdef LDAP_COMP_MATCH
+               a->ai_cr = NULL;
+#endif
+               a->ai_desc = ad;
+
+               if ( wi->wi_flags & WT_IS_OPEN ) {
+                       a->ai_indexmask = 0;
+                       a->ai_newmask = mask;
+               } else {
+                       a->ai_indexmask = mask;
+                       a->ai_newmask = 0;
+               }
+
+#ifdef LDAP_COMP_MATCH
+               if ( cr ) {
+                       a_cr = wt_attr_mask( wi, ad );
+                       if ( a_cr ) {
+                               /*
+                                * AttrInfo is already in AVL
+                                * just add the extracted component reference
+                                * in the AttrInfo
+                                */
+                               ch_free( a );
+                               rc = insert_component_reference( cr, &a_cr->ai_cr );
+                               if ( rc != LDAP_SUCCESS) {
+                                       fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
+                                       rc = LDAP_PARAM_ERROR;
+                                       goto fail;
+                               }
+                               continue;
+                       } else {
+                               rc = insert_component_reference( cr, &a->ai_cr );
+                               if ( rc != LDAP_SUCCESS) {
+                                       fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
+                                       rc = LDAP_PARAM_ERROR;
+                                       ch_free( a );
+                                       goto fail;
+                               }
+                       }
+               }
+#endif
+               rc = ainfo_insert( wi, a );
+               if( rc ) {
+                       if ( wi->wi_flags & WT_IS_OPEN ) {
+                               AttrInfo *b = wt_attr_mask( wi, ad );
+                               /* If there is already an index defined for this attribute
+                                * it must be replaced. Otherwise we end up with multiple
+                                * olcIndex values for the same attribute */
+                               if ( b->ai_indexmask & WT_INDEX_DELETING ) {
+                                       /* If we were editing this attr, reset it */
+                                       b->ai_indexmask &= ~WT_INDEX_DELETING;
+                                       /* If this is leftover from a previous add, commit it */
+                                       if ( b->ai_newmask )
+                                               b->ai_indexmask = b->ai_newmask;
+                                       b->ai_newmask = a->ai_newmask;
+                                       ch_free( a );
+                                       rc = 0;
+                                       continue;
+                               }
+                       }
+                       if (c_reply) {
+                               snprintf(c_reply->msg, sizeof(c_reply->msg),
+                                       "duplicate index definition for attr \"%s\"",
+                                       attrs[i] );
+                               fprintf( stderr, "%s: line %d: %s\n",
+                                       fname, lineno, c_reply->msg );
+                       }
+
+                       rc = LDAP_PARAM_ERROR;
+                       goto done;
+               }
+       }
+
+done:
+       ldap_charray_free( attrs );
+       if ( indexes != NULL ) ldap_charray_free( indexes );
+
+       return rc;
+}
+
+void
+wt_attr_info_free( AttrInfo *ai )
+{
+#ifdef LDAP_COMP_MATCH
+       free( ai->ai_cr );
+#endif
+       free( ai );
+}
+
+void
+wt_attr_index_destroy( struct wt_info *wi )
+{
+       int i;
+
+       for ( i=0; i<wi->wi_nattrs; i++ )
+               wt_attr_info_free( wi->wi_attrs[i] );
+
+       free( wi->wi_attrs );
+}
+
+
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/back-wt.h b/servers/slapd/back-wt/back-wt.h
new file mode 100644 (file)
index 0000000..1374232
--- /dev/null
@@ -0,0 +1,99 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#ifndef _BACK_WT_H_
+#define _BACK_WT_H_
+
+#include <portable.h>
+
+#include <ac/errno.h>
+#include <sys/stat.h>
+
+#include "slap.h"
+#include "wiredtiger.h"
+
+/* The default search IDL stack cache depth */
+#define DEFAULT_SEARCH_STACK_DEPTH  16
+
+struct wt_info {
+       WT_CONNECTION *wi_conn;
+       char *wi_dbenv_home;
+       char *wi_dbenv_config;
+       ID      wi_lastid;
+
+       slap_mask_t wi_defaultmask;
+       int         wi_nattrs;
+       struct wt_attrinfo **wi_attrs;
+       void *wi_search_stack;
+       int wi_search_stack_depth;
+
+       struct re_s *wi_index_task;
+
+       int wi_flags;
+#define WT_IS_OPEN      0x01
+#define WT_OPEN_INDEX   0x02
+#define WT_DEL_INDEX    0x08
+#define WT_RE_OPEN      0x10
+#define WT_NEED_UPGRADE 0x20
+};
+
+#define WT_TABLE_ID2ENTRY "table:id2entry"
+#define WT_TABLE_DN2ID "table:dn2id"
+
+#define WT_INDEX_DN "index:id2entry:dn"
+#define WT_INDEX_PID "index:dn2id:pid"
+#define WT_INDEX_REVDN "index:dn2id:revdn"
+
+#define ITEMzero(item) (memset((item), 0, sizeof(WT_ITEM)))
+#define ITEM2bv(item,bv) ((bv)->bv_val = (item)->data, \
+                                                 (bv)->bv_len = (item)->size)
+#define bv2ITEM(bv,item) ((item)->data = (bv)->bv_val, \
+                                                (item)->size = (bv)->bv_len )
+
+typedef struct {
+       WT_SESSION *session;
+} wt_ctx;
+
+/* for the cache of attribute information (which are indexed, etc.) */
+typedef struct wt_attrinfo {
+       AttributeDescription *ai_desc; /* attribute description cn;lang-en */
+       slap_mask_t ai_indexmask;   /* how the attr is indexed  */
+       slap_mask_t ai_newmask; /* new settings to replace old mask */
+       #ifdef LDAP_COMP_MATCH
+       ComponentReference* ai_cr; /*component indexing*/
+       #endif
+} AttrInfo;
+
+/* These flags must not clash with SLAP_INDEX flags or ops in slap.h! */
+#define        WT_INDEX_DELETING       0x8000U /* index is being modified */
+#define        WT_INDEX_UPDATE_OP      0x03    /* performing an index update */
+
+#include "proto-wt.h"
+
+#endif /* _BACK_WT_H_ */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/bind.c b/servers/slapd/back-wt/bind.c
new file mode 100644 (file)
index 0000000..e51243d
--- /dev/null
@@ -0,0 +1,156 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "back-wt.h"
+#include "config.h"
+
+int
+wt_bind( Operation *op, SlapReply *rs )
+{
+    struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       WT_SESSION *session;
+       wt_ctx *wc;
+       int rc;
+       Entry *e = NULL;
+       Attribute *a;
+       AttributeDescription *password = slap_schema.si_ad_userPassword;
+
+    Debug( LDAP_DEBUG_ARGS,
+                  "==> " LDAP_XSTRING(wt_bind) ": dn: %s\n",
+                  op->o_req_dn.bv_val, 0, 0);
+
+       /* allow noauth binds */
+       switch ( be_rootdn_bind( op, NULL ) ) {
+       case LDAP_SUCCESS:
+        /* frontend will send result */
+        return rs->sr_err = LDAP_SUCCESS;
+
+    default:
+        /* give the database a chance */
+        /* NOTE: this behavior departs from that of other backends,
+         * since the others, in case of password checking failure
+         * do not give the database a chance.  If an entry with
+         * rootdn's name does not exist in the database the result
+         * will be the same.  See ITS#4962 for discussion. */
+        break;
+       }
+
+       wc = wt_ctx_get(op, wi);
+       if( !wc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_bind)
+                          ": wt_ctx_get failed\n",
+                          0, 0, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "internal error";
+        send_ldap_result( op, rs );
+        return rs->sr_err;
+       }
+
+       /* get entry */
+       rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+       switch( rc ) {
+       case 0:
+               break;
+       case WT_NOTFOUND:
+               rs->sr_err = LDAP_INVALID_CREDENTIALS;
+               send_ldap_result( op, rs );
+               return rs->sr_err;
+       default:
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "internal error";
+        send_ldap_result( op, rs );
+        return rs->sr_err;
+       }
+
+       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:
+               rs->sr_err = LDAP_STRONG_AUTH_NOT_SUPPORTED;
+               rs->sr_text = "authentication method not supported";
+       }
+
+done:
+       /* free entry */
+       if (e) {
+               wt_entry_return(e);
+       }
+       if (rs->sr_err) {
+               send_ldap_result( op, rs );
+        if ( rs->sr_ref ) {
+            ber_bvarray_free( rs->sr_ref );
+                       rs->sr_ref = NULL;
+               }
+       }
+       return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/compare.c b/servers/slapd/back-wt/compare.c
new file mode 100644 (file)
index 0000000..dd34fba
--- /dev/null
@@ -0,0 +1,149 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "config.h"
+
+int
+wt_compare( Operation *op, SlapReply *rs )
+{
+    struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       Entry *e = NULL;
+       int manageDSAit = get_manageDSAit( op );
+       int rc;
+       wt_ctx *wc = NULL;
+
+       Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(wt_compare) ": %s\n",
+                  op->o_req_dn.bv_val, 0, 0 );
+
+       wc = wt_ctx_get(op, wi);
+       if( !wc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_compare)
+                          ": wt_ctx_get failed\n",
+                          0, 0, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "internal error";
+        send_ldap_result( op, rs );
+        return rs->sr_err;
+       }
+
+       rs->sr_err = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+       switch( rs->sr_err ) {
+       case 0:
+       case WT_NOTFOUND:
+               break;
+       default:
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "internal error";
+               goto return_results;
+       }
+
+       if ( rs->sr_err == WT_NOTFOUND ) {
+               if ( e != NULL ) {
+                       /* 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 {
+                               rs->sr_matched = ch_strdup( e->e_dn );
+                               if ( is_entry_referral( e )) {
+                                       BerVarray ref = get_entry_referrals( op, e );
+                                       rs->sr_ref = referral_rewrite( ref,
+                                                                                                  &e->e_name,
+                                                                                                  &op->o_req_dn,
+                                                                                                  LDAP_SCOPE_DEFAULT );
+                                       ber_bvarray_free( ref );
+                               } else {
+                                       rs->sr_ref = NULL;
+                               }
+                               rs->sr_err = LDAP_REFERRAL;
+                       }
+                       wt_entry_return( e );
+                       e = NULL;
+               } else {
+                       rs->sr_ref = referral_rewrite( default_referral,
+                                                                                  NULL,
+                                                                                  &op->o_req_dn,
+                                                                                  LDAP_SCOPE_DEFAULT );
+                       rs->sr_err = rs->sr_ref ? LDAP_REFERRAL : LDAP_NO_SUCH_OBJECT;
+               }
+
+               rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+               send_ldap_result( op, rs );
+               goto done;
+       }
+
+       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;
+               }
+
+               Debug( LDAP_DEBUG_TRACE, "entry is referral\n", 0, 0, 0 );
+
+               send_ldap_result( op, rs );
+
+               ber_bvarray_free( rs->sr_ref );
+               rs->sr_ref = NULL;
+               rs->sr_matched = NULL;
+               goto done;
+       }
+
+       rs->sr_err = slap_compare_entry( op, e, op->orc_ava );
+
+return_results:
+       send_ldap_result( op, rs );
+
+       switch ( rs->sr_err ) {
+       case LDAP_COMPARE_FALSE:
+       case LDAP_COMPARE_TRUE:
+               rs->sr_err = LDAP_SUCCESS;
+               break;
+       }
+
+done:
+       if ( e != NULL ) {
+               wt_entry_return( e );
+       }
+    return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/config.c b/servers/slapd/back-wt/config.c
new file mode 100644 (file)
index 0000000..6dca7f0
--- /dev/null
@@ -0,0 +1,158 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "config.h"
+
+#include "lutil.h"
+#include "ldap_rq.h"
+
+static ConfigDriver wt_cf_gen;
+
+enum {
+       WT_DIRECTORY = 1,
+       WT_CONFIG,
+       WT_INDEX,
+};
+
+static ConfigTable wtcfg[] = {
+    { "directory", "dir", 2, 2, 0, ARG_STRING|ARG_MAGIC|WT_DIRECTORY,
+         wt_cf_gen, "( OLcfgDbAt:0.1 NAME 'olcDbDirectory' "
+         "DESC 'Directory for database content' "
+         "EQUALITY caseIgnoreMatch "
+         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+    { "wtconfig", "config", 2, 2, 0, ARG_STRING|ARG_MAGIC|WT_CONFIG,
+         wt_cf_gen, "( OLcfgDbAt:13.1 NAME 'olcWtConfig' "
+         "DESC 'Configuration for WiredTiger' "
+         "EQUALITY caseIgnoreMatch "
+         "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+       { "index", "attr> <[pres,eq,approx,sub]", 2, 3, 0, ARG_MAGIC|WT_INDEX,
+         wt_cf_gen, "( OLcfgDbAt:0.2 NAME 'olcDbIndex' "
+         "DESC 'Attribute index parameters' "
+         "EQUALITY caseIgnoreMatch "
+         "SYNTAX OMsDirectoryString )", NULL, NULL },
+       { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+               NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs wtocs[] = {
+       { "( OLcfgDbOc:9.1 "
+         "NAME 'olcWtConfig' "
+         "DESC 'Wt backend ocnfiguration' "
+         "SUP olcDatabaseConfig "
+         "MUST olcDbDirectory "
+         "MAY ( olcWtConfig $ olcDbIndex ) )",
+         Cft_Database, wtcfg },
+       { NULL, 0, NULL }
+};
+
+/* reindex entries on the fly */
+static void *
+wt_online_index( void *ctx, void *arg )
+{
+       // Not implement yet
+}
+
+/* Cleanup loose ends after Modify completes */
+static int
+wt_cf_cleanup( ConfigArgs *c )
+{
+       // Not implement yet
+       return 0;
+}
+
+static int
+wt_cf_gen( ConfigArgs *c )
+{
+       struct wt_info *wi = (struct wt_info *) c->be->be_private;
+       int rc;
+
+       if(c->op == SLAP_CONFIG_EMIT) {
+               rc = 0;
+               // not implement yet
+               return rc;
+       }
+
+       switch( c->type ) {
+       case WT_DIRECTORY:
+               ch_free( wi->wi_dbenv_home );
+               wi->wi_dbenv_home = c->value_string;
+               break;
+       case WT_CONFIG:
+               ch_free( wi->wi_dbenv_config );
+               wi->wi_dbenv_config = c->value_string;
+               break;
+
+       case WT_INDEX:
+               rc = wt_attr_index_config( wi, c->fname, c->lineno,
+                                                                  c->argc - 1, &c->argv[1], &c->reply);
+
+               if( rc != LDAP_SUCCESS ) return 1;
+               wi->wi_flags |= WT_OPEN_INDEX;
+
+               if ( wi->wi_flags & WT_IS_OPEN ) {
+                       c->cleanup = wt_cf_cleanup;
+
+                       if ( !wi->wi_index_task ) {
+                               /* Start the task as soon as we finish here. Set a long
+                 * interval (10 hours) so that it only gets scheduled once.
+                 */
+                               if ( c->be->be_suffix == NULL || BER_BVISNULL( &c->be->be_suffix[0] ) ) {
+                                       fprintf( stderr, "%s: "
+                                                        "\"index\" must occur after \"suffix\".\n",
+                                                        c->log );
+                                       return 1;
+                               }
+                               ldap_pvt_thread_mutex_lock( &slapd_rq.rq_mutex );
+                               wi->wi_index_task = ldap_pvt_runqueue_insert(&slapd_rq, 36000,
+                                                                                                                        wt_online_index, c->be,
+                                                                                                                        LDAP_XSTRING(wt_online_index),
+                                                                                                                        c->be->be_suffix[0].bv_val );
+                               ldap_pvt_thread_mutex_unlock( &slapd_rq.rq_mutex );
+                       }
+               }
+               break;
+
+       }
+       return LDAP_SUCCESS;
+}
+
+int wt_back_init_cf( BackendInfo *bi )
+{
+       int rc;
+       bi->bi_cf_ocs = wtocs;
+
+       rc = config_register_schema( wtcfg, wtocs );
+       if ( rc ) return rc;
+       return 0;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/ctx.c b/servers/slapd/back-wt/ctx.c
new file mode 100644 (file)
index 0000000..3f4243a
--- /dev/null
@@ -0,0 +1,142 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "back-wt.h"
+#include "config.h"
+
+wt_ctx *
+wt_ctx_init(struct wt_info *wi)
+{
+       int rc;
+       wt_ctx *wc;
+
+       wc = ch_malloc( sizeof( wt_ctx ) );
+       if( !wc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_ctx_init)
+                          ": cannot allocate memory\n",
+                          0, 0, 0 );
+               return NULL;
+       }
+
+       memset(wc, 0, sizeof(wt_ctx));
+
+       if(!wc->session){
+               rc = wi->wi_conn->open_session(wi->wi_conn, NULL, NULL, &wc->session);
+               if( rc ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                  LDAP_XSTRING(wt_ctx_session)
+                                  ": open_session error %s(%d)\n",
+                                  wiredtiger_strerror(rc), rc, 0 );
+                       return NULL;
+               }
+       }
+       return wc;
+}
+
+void
+wt_ctx_free( void *key, void *data )
+{
+       wt_ctx *wc = data;
+
+       if(wc->session){
+               wc->session->close(wc->session, NULL);
+               wc->session = NULL;
+       }
+       ch_free(wc);
+}
+
+wt_ctx *
+wt_ctx_get(Operation *op, struct wt_info *wi){
+       int rc;
+       void *data;
+       wt_ctx *wc = NULL;
+
+       rc = ldap_pvt_thread_pool_getkey(op->o_threadctx,
+                                                                        wt_ctx_get, &data, NULL );
+       if( rc ){
+               wc = wt_ctx_init(wi);
+               if( !wc ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                  LDAP_XSTRING(wt_ctx)
+                                  ": wt_ctx_init failed\n",
+                                  0, 0, 0 );
+                       return NULL;
+               }
+               rc = ldap_pvt_thread_pool_setkey( op->o_threadctx,
+                                                                                 wt_ctx_get, wc, wt_ctx_free,
+                                                                                 NULL, NULL );
+               if( rc ) {
+                       Debug( LDAP_DEBUG_ANY, "wt_ctx: setkey error(%d)\n",
+                                  rc, 0, 0 );
+                       return NULL;
+               }
+               return wc;
+       }
+       return (wt_ctx *)data;
+}
+
+WT_CURSOR *
+wt_ctx_index_cursor(wt_ctx *wc, struct berval *name, int create)
+{
+       WT_CURSOR *cursor = NULL;
+       WT_SESSION *session = wc->session;
+       char tablename[1024];
+       int rc;
+
+       snprintf(tablename, sizeof(tablename), "table:%s", name->bv_val);
+
+       rc = session->open_cursor(session, tablename, NULL,
+                                                         "overwrite=false", &cursor);
+       if (rc == ENOENT && create) {
+               rc = session->create(session,
+                                                        tablename,
+                                                        "key_format=uQ,"
+                                                        "value_format=x,"
+                                                        "columns=(key, id, none)");
+               if( rc ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                  LDAP_XSTRING(indexer) ": table \"%s\": "
+                                  "cannot create idnex table: %s (%d)\n",
+                                  tablename, wiredtiger_strerror(rc), rc);
+                       return NULL;
+               }
+               rc = session->open_cursor(session, tablename, NULL,
+                                                                 "overwrite=false", &cursor);
+       }
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_id2entry_put)
+                          ": open cursor failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               return NULL;
+       }
+
+       return cursor;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/delete.c b/servers/slapd/back-wt/delete.c
new file mode 100644 (file)
index 0000000..f90c83d
--- /dev/null
@@ -0,0 +1,424 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "config.h"
+
+int
+wt_delete( Operation *op, SlapReply *rs )
+{
+    struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       struct berval   pdn = {0, NULL};
+       Entry *e = NULL;
+       Entry *p = NULL;
+       int manageDSAit = get_manageDSAit( op );
+       AttributeDescription *children = slap_schema.si_ad_children;
+       AttributeDescription *entry = slap_schema.si_ad_entry;
+
+       LDAPControl **preread_ctrl = NULL;
+       LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+       int num_ctrls = 0;
+
+       wt_ctx *wc;
+       int rc;
+       WT_CURSOR *cursor = NULL;
+
+       int parent_is_glue = 0;
+       int parent_is_leaf = 0;
+
+       Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(wt_delete) ": %s\n",
+                  op->o_req_dn.bv_val, 0, 0 );
+
+#ifdef LDAP_X_TXN
+       if( op->o_txnSpec && txn_preop( op, rs ))
+               return rs->sr_err;
+#endif
+
+       ctrls[num_ctrls] = 0;
+       rs->sr_text = NULL;
+
+       wc = wt_ctx_get(op, wi);
+       if( !wc ){
+        Debug( LDAP_DEBUG_TRACE,
+                          LDAP_XSTRING(wt_delete)
+                          ": wt_ctx_get failed\n",
+                          0, 0, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "internal error";
+               goto return_results;
+       }
+
+/* allocate CSN */
+       if ( BER_BVISNULL( &op->o_csn ) ) {
+               struct berval csn;
+               char csnbuf[LDAP_PVT_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_ndn, &pdn );
+       }
+
+       /* get parent */
+       rc = wt_dn2entry(op->o_bd, wc, &pdn, &p);
+       switch( rc ) {
+       case 0:
+       case WT_NOTFOUND:
+               break;
+       default:
+               /* TODO: error handling */
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "internal error";
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_delete)
+                          ": error at wt_dn2entry() rc=%d\n",
+                          rc, 0, 0 );
+               goto return_results;
+       }
+
+       if ( rc == WT_NOTFOUND && pdn.bv_len != 0 ) {
+               Debug( LDAP_DEBUG_ARGS,
+                          "<== " LDAP_XSTRING(wt_delete) ": no such object %s\n",
+                          op->o_req_dn.bv_val, 0, 0);
+
+               if ( p && !BER_BVISEMPTY( &p->e_name )) {
+                       rs->sr_matched = ch_strdup( p->e_name.bv_val );
+                       if ( is_entry_referral( p )) {
+                               BerVarray ref = get_entry_referrals( op, p );
+                               rs->sr_ref = referral_rewrite( ref, &p->e_name,
+                                                                                          &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+                               ber_bvarray_free( ref );
+                       } else {
+                               rs->sr_ref = NULL;
+                       }
+               } else {
+                       rs->sr_ref = referral_rewrite( default_referral, NULL,
+                                                                                  &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+               }
+
+               rs->sr_err = LDAP_REFERRAL;
+               rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+               goto return_results;
+       }
+
+       /* get entry */
+       rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+       switch( rc ) {
+       case 0:
+               break;
+       case WT_NOTFOUND:
+               Debug( LDAP_DEBUG_ARGS,
+                          "<== " LDAP_XSTRING(wt_delete)
+                          ": no such object %s\n",
+                          op->o_req_dn.bv_val, 0, 0);
+               rs->sr_err = LDAP_REFERRAL;
+               rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+               goto return_results;
+       default:
+               /* TODO: error handling */
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "internal error";
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_delete)
+                          ": error at wt_dn2entry() rc=%d\n",
+                          rc, 0, 0 );
+               goto return_results;
+       }
+
+       /* FIXME : dn2entry() should return non-glue entry */
+       if ( !manageDSAit && is_entry_glue( e ) ) {
+               Debug( LDAP_DEBUG_ARGS,
+                          "<== " LDAP_XSTRING(wt_delete)
+                          ": glue entry %s\n",
+                          op->o_req_dn.bv_val, 0, 0);
+
+               rs->sr_matched = ch_strdup( e->e_dn );
+               if ( is_entry_referral( e )) {
+                       BerVarray ref = get_entry_referrals( op, e );
+                       rs->sr_ref = referral_rewrite( ref, &e->e_name,
+                                                                                  &op->o_req_dn, LDAP_SCOPE_DEFAULT );
+                       ber_bvarray_free( ref );
+               } else {
+                       rs->sr_ref = NULL;
+               }
+
+               rs->sr_err = LDAP_REFERRAL;
+               rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+               goto return_results;
+       }
+
+       if ( pdn.bv_len != 0 ) {
+               /* 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(wt_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;
+               }
+
+       } else {
+               /* no parent, must be root to delete */
+               if( ! be_isroot( op ) ) {
+                       if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
+                                || be_shadow_update( op ) ) {
+                               p = (Entry *)&slap_entry_root;
+
+                               /* check parent for "children" acl */
+                               rs->sr_err = access_allowed( op, p,
+                                                                                        children, NULL, ACL_WDEL, NULL );
+
+                               p = NULL;
+
+                               if ( !rs->sr_err  ) {
+                                       Debug( LDAP_DEBUG_TRACE,
+                                                  "<== " LDAP_XSTRING(wt_delete)
+                                                  ": no access to parent\n",
+                                                  0, 0, 0 );
+                                       rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+                                       rs->sr_text = "no write access to parent";
+                                       goto return_results;
+                               }
+
+                       } else {
+                               Debug( LDAP_DEBUG_TRACE,
+                                          "<== " LDAP_XSTRING(wt_delete)
+                                          ": no parent and not root\n", 0, 0, 0 );
+                               rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+                               goto return_results;
+                       }
+               }
+       }
+
+       if ( get_assert( op ) &&
+                ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+       {
+               rs->sr_err = LDAP_ASSERTION_FAILED;
+               goto return_results;
+       }
+
+       rs->sr_err = access_allowed( op, e,
+                                                                entry, NULL, ACL_WDEL, NULL );
+       if ( !rs->sr_err  ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          "<== " LDAP_XSTRING(wt_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(tw_delete) ": entry is referral\n",
+                          0, 0, 0 );
+
+               rs->sr_err = LDAP_REFERRAL;
+               rs->sr_matched = ch_strdup( e->e_name.bv_val );
+               rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+               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(wt_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 */
+       rc = wt_dn2id_has_children( op, wc->session, e->e_id );
+       if( rc != WT_NOTFOUND ) {
+               switch( rc ) {
+               case 0:
+                       Debug(LDAP_DEBUG_ARGS,
+                                 "<== " LDAP_XSTRING(wt_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";
+                       break;
+               default:
+                       Debug(LDAP_DEBUG_ARGS,
+                                 "<== " LDAP_XSTRING(wt_delete)
+                                 ": has_children failed: %s (%d)\n",
+                                 wiredtiger_strerror(rc), rc, 0 );
+                       rs->sr_err = LDAP_OTHER;
+                       rs->sr_text = "internal error";
+               }
+               goto return_results;
+       }
+
+       /* begen transaction */
+       rc = wc->session->begin_transaction(wc->session, NULL);
+       if( rc ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          LDAP_XSTRING(wt_add) ": begin_transaction failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "begin_transaction failed";
+               goto return_results;
+       }
+
+       /* delete from dn2id */
+       rc = wt_dn2id_delete( op, wc->session, &e->e_nname);
+       if ( rc ) {
+               Debug(LDAP_DEBUG_TRACE,
+                         "<== " LDAP_XSTRING(wt_delete)
+                         ": dn2id failed: %s (%d)\n",
+                         wiredtiger_strerror(rc), rc, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "dn2id delete failed";
+               wc->session->rollback_transaction(wc->session, NULL);
+               goto return_results;
+       }
+
+       /* delete indices for old attributes */
+       rc = wt_index_entry_del( op, wc, e );
+       if ( rc ) {
+               Debug(LDAP_DEBUG_TRACE,
+                         "<== " LDAP_XSTRING(wt_delete)
+                         ": index delete failed: %s (%d)\n",
+                         wiredtiger_strerror(rc), rc, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "index delete failed";
+               wc->session->rollback_transaction(wc->session, NULL);
+               goto return_results;
+       }
+
+       /* fixup delete CSN */
+       if ( !SLAP_SHADOW( op->o_bd )) {
+               struct berval vals[2];
+
+               assert( !BER_BVISNULL( &op->o_csn ) );
+               vals[0] = op->o_csn;
+               BER_BVZERO( &vals[1] );
+               rs->sr_err = wt_index_values( op, wc->session, slap_schema.si_ad_entryCSN,
+                                                                         vals, 0, SLAP_INDEX_ADD_OP );
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       rs->sr_text = "entryCSN index update failed";
+                       rs->sr_err = LDAP_OTHER;
+                       wc->session->rollback_transaction(wc->session, NULL);
+                       goto return_results;
+               }
+       }
+
+       /* delete from id2entry */
+       rc = wt_id2entry_delete( op, wc->session, e );
+       if ( rc ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          "<== " LDAP_XSTRING(wt_delete)
+                          ": id2entry failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "entry delete failed";
+               wc->session->rollback_transaction(wc->session, NULL);
+               goto return_results;
+       }
+
+       if ( pdn.bv_len != 0 ) {
+               // TODO: glue entry
+       }
+
+       rc = wc->session->commit_transaction(wc->session, NULL);
+       if( rc ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          "<== " LDAP_XSTRING(wt_delete)
+                          ": commit_transaction failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               rs->sr_err = LDAP_OTHER;
+               rs->sr_text = "commit_transaction failed";
+               goto return_results;
+       }
+
+       Debug( LDAP_DEBUG_TRACE,
+                  LDAP_XSTRING(wt_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 ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
+               op->o_delete_glue_parent = 1;
+       }
+
+       if ( p != NULL ) {
+               wt_entry_return( p );
+       }
+
+       /* free entry */
+       if( e != NULL ) {
+               wt_entry_return( e );
+       }
+
+       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 );
+       }
+
+       /* TODO: checkpoint */
+
+       return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/dn2entry.c b/servers/slapd/back-wt/dn2entry.c
new file mode 100644 (file)
index 0000000..26e7166
--- /dev/null
@@ -0,0 +1,131 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "config.h"
+
+/*
+ * dn2entry - look up dn in the db and return the corresponding entry.
+ * No longer return closest ancestor, see wt_dn2pentry().
+ */
+int wt_dn2entry( BackendDB *be,
+                                wt_ctx *wc,
+                                struct berval *ndn,
+                                Entry **ep ){
+       uint64_t id;
+       WT_CURSOR *cursor = NULL;
+       WT_ITEM item;
+       EntryHeader eh;
+       int rc;
+       int eoff;
+       Entry *e = NULL;
+       WT_SESSION *session = wc->session;
+
+       if( ndn->bv_len == 0 ){
+               /* parent of root dn */
+               return WT_NOTFOUND;
+       }
+
+       rc = session->open_cursor(session,
+                                                         WT_INDEX_DN"(id, entry)",
+                                                         NULL, NULL, &cursor);
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2entry)
+                          ": open_cursor failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+
+       cursor->set_key(cursor, ndn->bv_val);
+       rc = cursor->search(cursor);
+       switch( rc ){
+       case 0:
+               break;
+       case WT_NOTFOUND:
+               goto done;
+       default:
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2entry)
+                          ": search failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+       cursor->get_value(cursor, &id, &item);
+       rc = wt_entry_header( &item,  &eh );
+
+       eoff = eh.data - (char *)item.data;
+       eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + item.size;
+       eh.bv.bv_val = ch_malloc( eh.bv.bv_len );
+       memset(eh.bv.bv_val, 0xff, eh.bv.bv_len);
+       eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
+       memcpy(eh.data, item.data, item.size);
+       eh.data += eoff;
+       rc = entry_decode( &eh, &e );
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2entry)
+                          ": entry decode error: %s (%d)\n",
+                          rc, 0, 0 );
+               goto done;
+       }
+
+       e->e_id = id;
+       *ep = e;
+
+done:
+       if(cursor){
+               cursor->close(cursor);
+       }
+       return rc;
+}
+
+/* dn2pentry - return parent entry */
+int wt_dn2pentry( BackendDB *be,
+                                 wt_ctx *wc,
+                                 struct berval *ndn,
+                                 Entry **ep ){
+       Entry *e = NULL;
+       struct berval pdn;
+       int rc;
+
+       if (be_issuffix( be, ndn )) {
+               *ep = NULL;
+               return WT_NOTFOUND;
+       }
+
+       dnParent( ndn, &pdn );
+       rc = wt_dn2entry(be, wc, &pdn, &e);
+       *ep = e;
+       return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/dn2id.c b/servers/slapd/back-wt/dn2id.c
new file mode 100644 (file)
index 0000000..30337d0
--- /dev/null
@@ -0,0 +1,393 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "back-wt.h"
+#include "config.h"
+#include "idl.h"
+
+char *
+mkrevdn(struct berval src){
+       char *dst, *p;
+       char *rdn;
+       size_t rdn_len;
+
+       p = dst = ch_malloc(src.bv_len + 2);
+       while(src.bv_len){
+               rdn = ber_bvrchr( &src, ',' );
+               if (rdn) {
+                       rdn_len = src.bv_len;
+                       src.bv_len = rdn - src.bv_val;
+                       rdn_len -= src.bv_len + 1;
+                       rdn++;
+               }else{
+                       /* first rdn */
+                       rdn_len = src.bv_len;
+                       rdn = src.bv_val;
+                       src.bv_len = 0;
+               }
+               AC_MEMCPY( p, rdn, rdn_len );
+               p += rdn_len;
+               *p++ = ',';
+       }
+       *p = '\0';
+       return dst;
+}
+
+int
+wt_dn2id_add(
+       Operation *op,
+       WT_SESSION *session,
+       ID pid,
+       Entry *e)
+{
+       int rc;
+       WT_CURSOR *cursor = NULL;
+       char *revdn = NULL;
+
+       Debug( LDAP_DEBUG_TRACE, "=> wt_dn2id_add 0x%lx: \"%s\"\n",
+                  e->e_id, e->e_ndn, 0 );
+       assert( e->e_id != NOID );
+
+       /* make reverse dn */
+       revdn = mkrevdn(e->e_nname);
+
+       rc = session->open_cursor(session, WT_TABLE_DN2ID, NULL,
+                                                         NULL, &cursor);
+       if(rc){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2id_add)
+                          ": open_cursor failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+    }
+       cursor->set_key(cursor, e->e_ndn);
+       cursor->set_value(cursor, e->e_id, pid, revdn);
+       rc = cursor->insert(cursor);
+       if(rc){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2id_add)
+                          ": insert failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+    }
+
+done:
+       if(revdn){
+               ch_free(revdn);
+       }
+       if(cursor){
+               cursor->close(cursor);
+       }
+       Debug( LDAP_DEBUG_TRACE, "<= wt_dn2id_add 0x%lx: %d\n", e->e_id, rc, 0 );
+       return rc;
+}
+
+int
+wt_dn2id_delete(
+       Operation *op,
+       WT_SESSION *session,
+       struct berval *ndn)
+{
+       int rc = 0;
+       WT_CURSOR *cursor = NULL;
+
+       Debug( LDAP_DEBUG_TRACE, "=> wt_dn2id_delete %s\n", ndn->bv_val, 0, 0 );
+
+       rc = session->open_cursor(session, WT_TABLE_DN2ID, NULL,
+                                                         NULL, &cursor);
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2id_delete)
+                          ": open_cursor failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+
+       cursor->set_key(cursor, ndn->bv_val);
+       rc = cursor->remove(cursor);
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2id_delete)
+                          ": remove failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+
+       Debug( LDAP_DEBUG_TRACE,
+                  "<= wt_dn2id_delete %s: %d\n",
+                  ndn->bv_val, rc, 0 );
+done:
+       if(cursor){
+               cursor->close(cursor);
+       }
+       return rc;
+}
+
+int
+wt_dn2id(
+       Operation *op,
+       WT_SESSION *session,
+    struct berval *ndn,
+    ID *id)
+{
+       WT_CURSOR *cursor = NULL;
+       struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       int rc;
+       ID nid;
+
+       Debug( LDAP_DEBUG_TRACE, "=> wt_dn2id(\"%s\")\n",
+                  ndn->bv_val, 0, 0 );
+
+       if ( ndn->bv_len == 0 ) {
+               *id = 0;
+               goto done;
+       }
+
+       rc = session->open_cursor(session, WT_TABLE_DN2ID
+                                                         "(id)",
+                              NULL, NULL, &cursor);
+       if( rc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2id)
+                          ": cursor open failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+
+       cursor->set_key(cursor, ndn->bv_val);
+       rc = cursor->search(cursor);
+       switch( rc ){
+       case 0:
+               break;
+       case WT_NOTFOUND:
+               goto done;
+       default:
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2id)
+                          ": search failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+       rc = cursor->get_value(cursor, id);
+       if( rc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2id)
+                          ": get_value failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+
+done:
+       if(cursor){
+               cursor->close(cursor);
+       }
+
+       if( rc ) {
+               Debug( LDAP_DEBUG_TRACE, "<= wt_dn2id: get failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+       } else {
+               Debug( LDAP_DEBUG_TRACE, "<= wt_dn2id: got id=0x%lx\n",
+                          *id, 0, 0 );
+       }
+
+       return rc;
+}
+
+int
+wt_dn2id_has_children(
+       Operation *op,
+       WT_SESSION *session,
+       ID id )
+{
+       struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       WT_CURSOR *cursor = NULL;
+       int rc;
+       uint64_t key = id;
+
+       rc = session->open_cursor(session, WT_INDEX_PID,
+                              NULL, NULL, &cursor);
+       if( rc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2id_has_children)
+                          ": cursor open failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+
+       cursor->set_key(cursor, key);
+       rc = cursor->search(cursor);
+
+done:
+       if(cursor){
+               cursor->close(cursor);
+       }
+
+       return rc;
+}
+
+int
+wt_dn2idl(
+       Operation *op,
+       WT_SESSION *session,
+       struct berval *ndn,
+       Entry *e,
+       ID *ids,
+       ID *stack)
+{
+       struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       WT_CURSOR *cursor = NULL;
+       int exact = 0;
+       int rc;
+       char *revdn = NULL;
+       size_t revdn_len;
+       char *key;
+       ID id, pid;
+
+       Debug( LDAP_DEBUG_TRACE,
+                  "=> wt_dn2idl(\"%s\")\n",
+                  ndn->bv_val, 0, 0 );
+
+       if(op->ors_scope != LDAP_SCOPE_ONELEVEL &&
+          be_issuffix( op->o_bd, &e->e_nname )){
+               WT_IDL_ALL(wi, ids);
+               return 0;
+       }
+
+       revdn = mkrevdn(*ndn);
+       revdn_len = strlen(revdn);
+       rc = session->open_cursor(session, WT_INDEX_REVDN"(id, pid)",
+                              NULL, NULL, &cursor);
+       if( rc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2idl)
+                          ": cursor open failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+       cursor->set_key(cursor, revdn);
+       rc = cursor->search_near(cursor, &exact);
+       if( rc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2idl)
+                          ": search failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+
+       do {
+               rc = cursor->get_key(cursor, &key);
+               if( rc ){
+                       Debug( LDAP_DEBUG_ANY,
+                                  LDAP_XSTRING(wt_dn2idl)
+                                  ": get_key failed: %s (%d)\n",
+                                  wiredtiger_strerror(rc), rc, 0 );
+                       goto done;
+               }
+
+               if( strncmp(revdn, key, revdn_len) ){
+                       if(exact < 0){
+                               rc = cursor->next(cursor);
+                               if (rc) {
+                                       break;
+                               }else{
+                                       continue;
+                               }
+                       }
+                       break;
+               }
+               exact = 0;
+               rc = cursor->get_value(cursor, &id, &pid);
+               if( rc ){
+                       Debug( LDAP_DEBUG_ANY,
+                                  LDAP_XSTRING(wt_dn2id)
+                                  ": get_value failed: %s (%d)\n",
+                                  wiredtiger_strerror(rc), rc, 0 );
+                       goto done;
+               }
+               if( op->ors_scope == LDAP_SCOPE_ONELEVEL &&
+                       e->e_id != pid){
+                       rc = cursor->next(cursor);
+                       if ( rc ) {
+                               break;
+                       }
+                       continue;
+               }else{
+                       wt_idl_append_one(ids, id);
+               }
+               rc = cursor->next(cursor);
+       }while(rc == 0);
+
+       if (rc == WT_NOTFOUND ) {
+               rc = LDAP_SUCCESS;
+       }
+
+done:
+       if(revdn){
+               ch_free(revdn);
+       }
+       if(cursor){
+               cursor->close(cursor);
+       }
+       return rc;
+}
+
+#if 0
+int
+wt_dn2id(
+       Operation *op,
+       WT_SESSION *session,
+    struct berval *dn,
+    ID *id)
+{
+       struct wt_info *wi = (struct wy_info *) op->o_bd->be_private;
+       WT_CURSOR *cursor = NULL;
+       int rc;
+       Debug( LDAP_DEBUG_TRACE, "=> wt_dn2id(\"%s\")\n", dn->bv_val, 0, 0 );
+
+       rc = session->open_cursor(session, WT_INDEX_DN"(id)",
+                              NULL, NULL, &cursor);
+       if( rc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2id)
+                          ": cursor open failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               return rc;
+       }
+       cursor->set_key(cursor, dn->bv_val);
+       rc = cursor->search(cursor);
+       if( !rc ){
+               cursor->get_key(cursor, &id);
+       }
+       cursor->close(cursor);
+       return rc;
+}
+#endif
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/filterindex.c b/servers/slapd/back-wt/filterindex.c
new file mode 100644 (file)
index 0000000..17aa7c2
--- /dev/null
@@ -0,0 +1,679 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "idl.h"
+
+static int
+presence_candidates(
+       Operation *op,
+       wt_ctx *wc,
+       AttributeDescription *desc,
+       ID *ids )
+{
+       struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       slap_mask_t mask;
+       struct berval prefix = {0, NULL};
+       int rc;
+       WT_CURSOR *cursor = NULL;
+
+       Debug( LDAP_DEBUG_TRACE, "=> wt_presence_candidates (%s)\n",
+                  desc->ad_cname.bv_val, 0, 0 );
+
+       WT_IDL_ALL( wi, ids );
+
+       if( desc == slap_schema.si_ad_objectClass ) {
+               return 0;
+       }
+
+       rc = wt_index_param( op->o_bd, desc, LDAP_FILTER_PRESENT,
+                                                &mask, &prefix );
+
+       if( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+               /* not indexed */
+               Debug( LDAP_DEBUG_TRACE,
+                          "<= wt_presence_candidates: (%s) not indexed\n",
+                          desc->ad_cname.bv_val, 0, 0 );
+               return 0;
+       }
+
+       if( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          "<= wt_presence_candidates: (%s) index_param "
+                          "returned=%d\n",
+                          desc->ad_cname.bv_val, rc, 0 );
+               return 0;
+       }
+
+       if( prefix.bv_val == NULL ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          "<= wt_presence_candidates: (%s) no prefix\n",
+                          desc->ad_cname.bv_val, 0, 0 );
+               return -1;
+       }
+
+       /* open index cursor */
+       cursor = wt_ctx_index_cursor(wc, &desc->ad_type->sat_cname, 0);
+       if( !cursor ) {
+               Debug( LDAP_DEBUG_ANY,
+                          "<= wt_presence_candidates: open index cursor failed: %s\n",
+                          desc->ad_type->sat_cname.bv_val, 0, 0 );
+               return 0;
+       }
+
+       rc = wt_key_read( op->o_bd, cursor, &prefix, ids, NULL, 0 );
+
+       if(cursor){
+               cursor->close(cursor);
+       }
+       Debug(LDAP_DEBUG_TRACE,
+                 "<= wt_presence_candidates: id=%ld first=%ld last=%ld\n",
+                 (long) ids[0],
+                 (long) WT_IDL_FIRST(ids),
+                 (long) WT_IDL_LAST(ids) );
+
+       return 0;
+}
+
+static int
+equality_candidates(
+       Operation *op,
+       wt_ctx *wc,
+       AttributeAssertion *ava,
+       ID *ids,
+       ID *tmp)
+{
+       struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       slap_mask_t mask;
+       struct berval prefix = {0, NULL};
+       struct berval *keys = NULL;
+       int i;
+       int rc;
+       MatchingRule *mr;
+       WT_CURSOR *cursor = NULL;
+
+       Debug( LDAP_DEBUG_TRACE, "=> wt_equality_candidates (%s)\n",
+                  ava->aa_desc->ad_cname.bv_val, 0, 0 );
+
+       if ( ava->aa_desc == slap_schema.si_ad_entryDN ) {
+               ID id = NOID;
+               rc = wt_dn2id(op, wc->session, &ava->aa_value, &id);
+               if( rc == 0 ){
+                       wt_idl_append_one(ids, id);
+               }else if ( rc == WT_NOTFOUND ) {
+                       WT_IDL_ZERO( ids );
+                       rc = 0;
+               }
+               return rc;
+       }
+
+       WT_IDL_ALL( wi, ids );
+
+       rc = wt_index_param( op->o_bd, ava->aa_desc, LDAP_FILTER_EQUALITY,
+                                                &mask, &prefix );
+
+       if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+               Debug( LDAP_DEBUG_ANY,
+                          "<= wt_equality_candidates: (%s) not indexed\n",
+                          ava->aa_desc->ad_cname.bv_val, 0, 0 );
+               return 0;
+       }
+
+       if( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY,
+                          "<= wt_equality_candidates: (%s) index_param failed (%d)\n",
+                          ava->aa_desc->ad_cname.bv_val, rc, 0 );
+               return 0;
+       }
+
+       mr = ava->aa_desc->ad_type->sat_equality;
+       if( !mr ) {
+               return 0;
+       }
+
+       if( !mr->smr_filter ) {
+               return 0;
+       }
+
+       rc = (mr->smr_filter)(
+               LDAP_FILTER_EQUALITY,
+               mask,
+               ava->aa_desc->ad_type->sat_syntax,
+               mr,
+               &prefix,
+               &ava->aa_value,
+               &keys, op->o_tmpmemctx );
+
+       if( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          "<= wt_equality_candidates: (%s, %s) "
+                          "MR filter failed (%d)\n",
+                          prefix.bv_val, ava->aa_desc->ad_cname.bv_val, rc );
+               return 0;
+       }
+
+       if( keys == NULL ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          "<= wt_equality_candidates: (%s) no keys\n",
+                          ava->aa_desc->ad_cname.bv_val, 0, 0 );
+               return 0;
+       }
+
+       /* open index cursor */
+       cursor = wt_ctx_index_cursor(wc, &ava->aa_desc->ad_type->sat_cname, 0);
+       if( !cursor ) {
+               Debug( LDAP_DEBUG_ANY,
+                          "<= wt_equality_candidates: open index cursor failed: %s\n",
+                          ava->aa_desc->ad_type->sat_cname.bv_val, 0, 0 );
+               return 0;
+       }
+
+       for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+               rc = wt_key_read( op->o_bd, cursor, &keys[i], tmp, NULL, 0 );
+               if( rc == WT_NOTFOUND ) {
+                       WT_IDL_ZERO( ids );
+                       rc = 0;
+                       break;
+               } else if( rc != LDAP_SUCCESS ) {
+                       Debug( LDAP_DEBUG_TRACE,
+                                  "<= wt_equality_candidates: (%s) "
+                                  "key read failed (%d)\n",
+                                  ava->aa_desc->ad_cname.bv_val, rc, 0 );
+                       break;
+               }
+               if ( i == 0 ) {
+                       WT_IDL_CPY( ids, tmp );
+               } else {
+                       wt_idl_intersection( ids, tmp );
+               }
+
+               if( WT_IDL_IS_ZERO( ids ) )
+                       break;
+       }
+
+       ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+       if(cursor){
+               cursor->close(cursor);
+       }
+
+       Debug( LDAP_DEBUG_TRACE,
+                  "<= wt_equality_candidates: id=%ld, first=%ld, last=%ld\n",
+                  (long) ids[0],
+                  (long) WT_IDL_FIRST(ids),
+                  (long) WT_IDL_LAST(ids) );
+
+       return rc;
+}
+
+static int
+approx_candidates(
+       Operation *op,
+       wt_ctx *wc,
+       AttributeAssertion *ava,
+       ID *ids,
+       ID *tmp )
+{
+       struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       int i;
+       int rc;
+    slap_mask_t mask;
+       struct berval prefix = {0, NULL};
+       struct berval *keys = NULL;
+       MatchingRule *mr;
+       WT_CURSOR *cursor = NULL;
+
+       Debug( LDAP_DEBUG_TRACE, "=> wt_approx_candidates (%s)\n",
+                  ava->aa_desc->ad_cname.bv_val, 0, 0 );
+
+       WT_IDL_ALL( wi, ids );
+
+       rc = wt_index_param( op->o_bd, ava->aa_desc, LDAP_FILTER_APPROX,
+                                                &mask, &prefix );
+
+       if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+               Debug( LDAP_DEBUG_ANY,
+                          "<= wt_approx_candidates: (%s) not indexed\n",
+                          ava->aa_desc->ad_cname.bv_val, 0, 0 );
+               return 0;
+       }
+
+       if( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY,
+                          "<= wt_approx_candidates: (%s) index_param failed (%d)\n",
+                          ava->aa_desc->ad_cname.bv_val, rc, 0 );
+               return 0;
+       }
+
+       mr = ava->aa_desc->ad_type->sat_approx;
+       if( !mr ) {
+               /* no approx matching rule, try equality matching rule */
+               mr = ava->aa_desc->ad_type->sat_equality;
+       }
+
+       if( !mr ) {
+               return 0;
+       }
+
+       if( !mr->smr_filter ) {
+               return 0;
+       }
+
+       rc = (mr->smr_filter)(
+               LDAP_FILTER_APPROX,
+               mask,
+               ava->aa_desc->ad_type->sat_syntax,
+               mr,
+               &prefix,
+               &ava->aa_value,
+               &keys, op->o_tmpmemctx );
+
+       if( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          "<= wt_approx_candidates: (%s, %s) MR filter failed (%d)\n",
+                          prefix.bv_val, ava->aa_desc->ad_cname.bv_val, rc );
+               return 0;
+       }
+
+       if( keys == NULL ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          "<= wt_approx_candidates: (%s) no keys (%s)\n",
+                          prefix.bv_val, ava->aa_desc->ad_cname.bv_val, 0 );
+               return 0;
+       }
+
+       /* open index cursor */
+       cursor = wt_ctx_index_cursor(wc, &ava->aa_desc->ad_type->sat_cname, 0);
+       if( !cursor ) {
+               Debug( LDAP_DEBUG_ANY,
+                          "<= wt_approx_candidates: open index cursor failed: %s\n",
+                          ava->aa_desc->ad_type->sat_cname.bv_val, 0, 0 );
+               return 0;
+       }
+
+       for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+               rc = wt_key_read( op->o_bd, cursor, &keys[i], tmp, NULL, 0 );
+               if( rc == WT_NOTFOUND ) {
+                       WT_IDL_ZERO( ids );
+                       rc = 0;
+                       break;
+               } else if( rc != LDAP_SUCCESS ) {
+                       Debug( LDAP_DEBUG_TRACE,
+                                  "<= wt_approx_candidates: (%s) key read failed (%d)\n",
+                                  ava->aa_desc->ad_cname.bv_val, rc, 0 );
+                       break;
+               }
+
+               if( WT_IDL_IS_ZERO( tmp ) ) {
+                       Debug( LDAP_DEBUG_TRACE,
+                                  "<= wt_approx_candidates: (%s) NULL\n",
+                                  ava->aa_desc->ad_cname.bv_val, 0, 0 );
+                       WT_IDL_ZERO( ids );
+                       break;
+               }
+
+               if ( i == 0 ) {
+                       WT_IDL_CPY( ids, tmp );
+               } else {
+                       wt_idl_intersection( ids, tmp );
+               }
+
+               if( WT_IDL_IS_ZERO( ids ) )
+                       break;
+       }
+
+       ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+       if(cursor){
+               cursor->close(cursor);
+       }
+
+       Debug( LDAP_DEBUG_TRACE,
+                  "<= wt_approx_candidates %ld, first=%ld, last=%ld\n",
+                  (long) ids[0],
+                  (long) WT_IDL_FIRST(ids),
+                  (long) WT_IDL_LAST(ids) );
+
+       return rc;
+}
+
+static int
+substring_candidates(
+       Operation *op,
+       wt_ctx *wc,
+       SubstringsAssertion *sub,
+       ID *ids,
+       ID *tmp )
+{
+       struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       int i;
+       int rc;
+    slap_mask_t mask;
+       struct berval prefix = {0, NULL};
+       struct berval *keys = NULL;
+       MatchingRule *mr;
+       WT_CURSOR *cursor = NULL;
+
+       Debug( LDAP_DEBUG_TRACE, "=> wt_substring_candidates (%s)\n",
+                  sub->sa_desc->ad_cname.bv_val, 0, 0 );
+
+       WT_IDL_ALL( wi, ids );
+
+       rc = wt_index_param( op->o_bd, sub->sa_desc, LDAP_FILTER_SUBSTRINGS,
+                                                &mask, &prefix );
+
+       if ( rc == LDAP_INAPPROPRIATE_MATCHING ) {
+               Debug( LDAP_DEBUG_ANY,
+                          "<= wt_substring_candidates: (%s) not indexed\n",
+                          sub->sa_desc->ad_cname.bv_val, 0, 0 );
+               return 0;
+       }
+
+       if( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_ANY,
+                          "<= wt_substring_candidates: (%s) "
+                          "index_param failed (%d)\n",
+                          sub->sa_desc->ad_cname.bv_val, rc, 0 );
+               return 0;
+       }
+
+       mr = sub->sa_desc->ad_type->sat_substr;
+
+       if( !mr ) {
+               return 0;
+       }
+
+       if( !mr->smr_filter ) {
+               return 0;
+       }
+
+       rc = (mr->smr_filter)(
+               LDAP_FILTER_SUBSTRINGS,
+               mask,
+               sub->sa_desc->ad_type->sat_syntax,
+               mr,
+               &prefix,
+               sub,
+               &keys, op->o_tmpmemctx );
+
+       if( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          "<= wt_substring_candidates: (%s) MR filter failed (%d)\n",
+                          sub->sa_desc->ad_cname.bv_val, rc, 0 );
+               return 0;
+       }
+
+       if( keys == NULL ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          "<= wt_substring_candidates: (0x%04lx) no keys (%s)\n",
+                          mask, sub->sa_desc->ad_cname.bv_val, 0 );
+               return 0;
+       }
+
+       /* open index cursor */
+       cursor = wt_ctx_index_cursor(wc, &sub->sa_desc->ad_cname, 0);
+       if( !cursor ) {
+               Debug( LDAP_DEBUG_ANY,
+                          "<= wt_substring_candidates: open index cursor failed: %s\n",
+                          sub->sa_desc->ad_cname.bv_val, 0, 0 );
+               return 0;
+       }
+
+       for ( i= 0; keys[i].bv_val != NULL; i++ ) {
+               rc = wt_key_read( op->o_bd, cursor, &keys[i], tmp, NULL, 0 );
+
+               if( rc == WT_NOTFOUND ) {
+                       WT_IDL_ZERO( ids );
+                       rc = 0;
+                       break;
+               } else if( rc != LDAP_SUCCESS ) {
+                       Debug( LDAP_DEBUG_TRACE,
+                                  "<= wt_substring_candidates: (%s) key read failed (%d)\n",
+                                  sub->sa_desc->ad_cname.bv_val, rc, 0 );
+                       break;
+               }
+
+               if( WT_IDL_IS_ZERO( tmp ) ) {
+                       Debug( LDAP_DEBUG_TRACE,
+                                  "<= wt_substring_candidates: (%s) NULL\n",
+                                  sub->sa_desc->ad_cname.bv_val, 0, 0 );
+                       WT_IDL_ZERO( ids );
+                       break;
+               }
+
+               if ( i == 0 ) {
+                       WT_IDL_CPY( ids, tmp );
+               } else {
+                       wt_idl_intersection( ids, tmp );
+               }
+
+               if( WT_IDL_IS_ZERO( ids ) )
+                       break;
+       }
+
+       ber_bvarray_free_x( keys, op->o_tmpmemctx );
+
+       if(cursor){
+               cursor->close(cursor);
+       }
+
+       Debug( LDAP_DEBUG_TRACE,
+                  "<= wt_substring_candidates: %ld, first=%ld, last=%ld\n",
+                  (long) ids[0],
+                  (long) WT_IDL_FIRST(ids),
+                  (long) WT_IDL_LAST(ids));
+       return rc;
+}
+
+
+static int
+list_candidates(
+       Operation *op,
+       wt_ctx *wc,
+       Filter *flist,
+       int ftype,
+       ID *ids,
+       ID *tmp,
+       ID *save )
+{
+       int rc = 0;
+       Filter  *f;
+
+       Debug( LDAP_DEBUG_FILTER, "=> wt_list_candidates 0x%x\n", ftype, 0, 0 );
+       for ( f = flist; f != NULL; f = f->f_next ) {
+               /* ignore precomputed scopes */
+               if ( f->f_choice == SLAPD_FILTER_COMPUTED &&
+                        f->f_result == LDAP_SUCCESS ) {
+                       continue;
+               }
+               WT_IDL_ZERO( save );
+               rc = wt_filter_candidates( op, wc, f, save, tmp,
+                                                                  save+WT_IDL_UM_SIZE );
+
+               if ( rc != 0 ) {
+                       /* TODO: error handling */
+                       /*
+                       if ( rc == DB_LOCK_DEADLOCK )
+                               return rc;
+                       */
+                       if ( ftype == LDAP_FILTER_AND ) {
+                               rc = 0;
+                               continue;
+                       }
+                       break;
+               }
+
+
+               if ( ftype == LDAP_FILTER_AND ) {
+                       if ( f == flist ) {
+                               WT_IDL_CPY( ids, save );
+                       } else {
+                               wt_idl_intersection( ids, save );
+                       }
+                       if( WT_IDL_IS_ZERO( ids ) )
+                               break;
+               } else {
+                       if ( f == flist ) {
+                               WT_IDL_CPY( ids, save );
+                       } else {
+                               wt_idl_union( ids, save );
+                       }
+               }
+       }
+
+       if( rc == LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_FILTER,
+                          "<= wt_list_candidates: id=%ld first=%ld last=%ld\n",
+                          (long) ids[0],
+                          (long) WT_IDL_FIRST(ids),
+                          (long) WT_IDL_LAST(ids) );
+
+       } else {
+               Debug( LDAP_DEBUG_FILTER,
+                          "<= wt_list_candidates: undefined rc=%d\n",
+                          rc, 0, 0 );
+       }
+
+       return 0;
+}
+
+int
+wt_filter_candidates(
+       Operation *op,
+       wt_ctx *wc,
+       Filter *f,
+       ID *ids,
+       ID *tmp,
+       ID *stack )
+{
+       struct wt_info *wi = (struct wt_info *)op->o_bd->be_private;
+       int rc = 0;
+       Debug( LDAP_DEBUG_FILTER, "=> wt_filter_candidates\n", 0, 0, 0 );
+
+       if ( f->f_choice & SLAPD_FILTER_UNDEFINED ) {
+               WT_IDL_ZERO( ids );
+               goto done;
+       }
+
+       switch ( f->f_choice ) {
+       case SLAPD_FILTER_COMPUTED:
+               switch( f->f_result ) {
+               case SLAPD_COMPARE_UNDEFINED:
+                       /* This technically is not the same as FALSE, but it
+                        * certainly will produce no matches.
+                        */
+                       /* FALL THRU */
+               case LDAP_COMPARE_FALSE:
+                       WT_IDL_ZERO( ids );
+                       break;
+               case LDAP_COMPARE_TRUE: {
+
+                       WT_IDL_ALL( wi, ids );
+               } break;
+               case LDAP_SUCCESS:
+                       /* this is a pre-computed scope, leave it alone */
+                       break;
+               }
+               break;
+       case LDAP_FILTER_PRESENT:
+               Debug( LDAP_DEBUG_FILTER, "\tPRESENT\n", 0, 0, 0 );
+               rc = presence_candidates( op, wc, f->f_desc, ids );
+               break;
+
+       case LDAP_FILTER_EQUALITY:
+               Debug( LDAP_DEBUG_FILTER, "\tEQUALITY\n", 0, 0, 0 );
+               rc = equality_candidates( op, wc, f->f_ava, ids, tmp );
+               break;
+
+       case LDAP_FILTER_APPROX:
+               Debug( LDAP_DEBUG_FILTER, "\tAPPROX\n", 0, 0, 0 );
+               rc = approx_candidates( op, wc, f->f_ava, ids, tmp );
+               break;
+
+       case LDAP_FILTER_SUBSTRINGS:
+               Debug( LDAP_DEBUG_FILTER, "\tSUBSTRINGS\n", 0, 0, 0 );
+               rc = substring_candidates( op, wc, f->f_sub, ids, tmp );
+               break;
+
+       case LDAP_FILTER_GE:
+               /* if no GE index, use pres */
+               /* TODO: not implement yet */
+               rc = presence_candidates( op, wc, f->f_ava->aa_desc, ids );
+               break;
+
+    case LDAP_FILTER_LE:
+               /* if no LE index, use pres */
+               /* TODO: not implement yet */
+               Debug( LDAP_DEBUG_FILTER, "\tLE\n", 0, 0, 0 );
+               rc = presence_candidates( op, wc, f->f_ava->aa_desc, ids );
+               break;
+
+       case LDAP_FILTER_NOT:
+               /* no indexing to support NOT filters */
+               Debug( LDAP_DEBUG_FILTER, "\tNOT\n", 0, 0, 0 );
+               WT_IDL_ALL( wi, ids );
+               break;
+
+       case LDAP_FILTER_AND:
+               Debug( LDAP_DEBUG_FILTER, "\tAND\n", 0, 0, 0 );
+               rc = list_candidates( op, wc,
+                                                         f->f_and, LDAP_FILTER_AND, ids, tmp, stack );
+               break;
+
+       case LDAP_FILTER_OR:
+               Debug( LDAP_DEBUG_FILTER, "\tOR\n", 0, 0, 0 );
+               rc = list_candidates( op, wc,
+                                                         f->f_or, LDAP_FILTER_OR, ids, tmp, stack );
+               break;
+
+       case LDAP_FILTER_EXT:
+               /* TODO: not implement yet */
+               Debug( LDAP_DEBUG_FILTER, "\tEXT\n", 0, 0, 0 );
+               rc = presence_candidates( op, wc, f->f_ava->aa_desc, ids );
+               break;
+
+       default:
+               Debug( LDAP_DEBUG_FILTER, "\tUNKNOWN %lu\n",
+                          (unsigned long) f->f_choice, 0, 0 );
+               /* Must not return NULL, otherwise extended filters break */
+               WT_IDL_ALL( wi, ids );
+       }
+
+done:
+       Debug( LDAP_DEBUG_FILTER,
+                  "<= wt_filter_candidates: id=%ld first=%ld last=%ld\n",
+                  (long) ids[0],
+                  (long) WT_IDL_FIRST( ids ),
+                  (long) WT_IDL_LAST( ids ) );
+       return 0;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/id2entry.c b/servers/slapd/back-wt/id2entry.c
new file mode 100644 (file)
index 0000000..e135d18
--- /dev/null
@@ -0,0 +1,241 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "back-wt.h"
+#include "config.h"
+
+static int wt_id2entry_put(
+       Operation *op,
+       WT_SESSION *session,
+       Entry *e,
+       const char *config )
+{
+       struct berval bv;
+       WT_CURSOR *cursor = NULL;
+       WT_ITEM item;
+       int rc;
+
+       rc = entry_encode( e, &bv );
+       if(rc != LDAP_SUCCESS){
+               return -1;
+       }
+       item.size = bv.bv_len;
+       item.data = bv.bv_val;
+
+       rc = session->open_cursor(session, WT_TABLE_ID2ENTRY, NULL,
+                                                         config, &cursor);
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_id2entry_put)
+                          ": open_cursor failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+       cursor->set_key(cursor, e->e_id);
+       cursor->set_value(cursor, e->e_ndn, &item);
+       rc = cursor->insert(cursor);
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_id2entry_put)
+                          ": insert failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+
+done:
+       ch_free( bv.bv_val );
+       if(cursor){
+               cursor->close(cursor);
+       }
+       return rc;
+}
+
+int wt_id2entry_add(
+       Operation *op,
+       WT_SESSION *session,
+       Entry *e )
+{
+       return wt_id2entry_put(op, session, e, "overwrite=false");
+}
+
+int wt_id2entry_update(
+       Operation *op,
+       WT_SESSION *session,
+       Entry *e )
+{
+       return wt_id2entry_put(op, session, e, "overwrite=true");
+}
+
+int wt_id2entry_delete(
+       Operation *op,
+       WT_SESSION *session,
+       Entry *e )
+{
+       int rc;
+       WT_CURSOR *cursor = NULL;
+       rc = session->open_cursor(session, WT_TABLE_ID2ENTRY, NULL,
+                                                         NULL, &cursor);
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_id2entry_delete)
+                          ": open_cursor failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+       cursor->set_key(cursor, e->e_id);
+       rc = cursor->remove(cursor);
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_id2entry_delete)
+                          ": remove failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+
+done:
+       if(cursor){
+               cursor->close(cursor);
+       }
+       return rc;
+}
+
+int wt_id2entry( BackendDB *be,
+                                WT_SESSION *session,
+                                ID id,
+                                Entry **ep ){
+       int rc;
+       WT_CURSOR *cursor = NULL;
+       WT_ITEM item;
+       EntryHeader eh;
+       int eoff;
+       Entry *e = NULL;
+
+       rc = session->open_cursor(session, WT_TABLE_ID2ENTRY"(entry)", NULL,
+                                                         NULL, &cursor);
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_id2entry)
+                          ": open_cursor failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+
+       cursor->set_key(cursor, id);
+       rc = cursor->search(cursor);
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_id2entry)
+                          ": search failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+
+       cursor->get_value(cursor, &item);
+       rc = wt_entry_header( &item,  &eh );
+       eoff = eh.data - (char *)item.data;
+       eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + item.size;
+       eh.bv.bv_val = ch_malloc( eh.bv.bv_len );
+       memset(eh.bv.bv_val, 0xff, eh.bv.bv_len);
+       eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
+       memcpy(eh.data, item.data, item.size);
+       eh.data += eoff;
+       rc = entry_decode( &eh, &e );
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_id2entry)
+                          ": entry decode error: %s (%d)\n",
+                          rc, 0, 0 );
+               goto done;
+       }
+       e->e_id = id;
+       *ep = e;
+
+done:
+       if(cursor){
+               cursor->close(cursor);
+       }
+       return rc;
+}
+
+int wt_entry_return(
+       Entry *e
+       )
+{
+       if ( !e ) {
+               return 0;
+       }
+
+    /* Our entries are allocated in two blocks; the data comes from
+        * the db itself and the Entry structure and associated pointers
+        * are allocated in entry_decode. The db data pointer is saved
+        * in e_bv.
+        */
+       if ( e->e_bv.bv_val ) {
+#if 0
+               /* See if the DNs were changed by modrdn */
+               if( e->e_nname.bv_val < e->e_bv.bv_val || e->e_nname.bv_val >
+                       e->e_bv.bv_val + e->e_bv.bv_len ) {
+                       ch_free(e->e_name.bv_val);
+                       ch_free(e->e_nname.bv_val);
+               }
+#endif
+               e->e_name.bv_val = NULL;
+               e->e_nname.bv_val = NULL;
+               /* In tool mode the e_bv buffer is realloc'd, leave it alone */
+               if( !(slapMode & SLAP_TOOL_MODE) ) {
+                       free( e->e_bv.bv_val );
+               }
+               BER_BVZERO( &e->e_bv );
+       }
+
+       entry_free( e );
+}
+
+int wt_entry_release(
+       Operation *op,
+       Entry *e,
+       int rw )
+{
+       struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       return wt_entry_return( e );
+}
+
+/*
+ * return LDAP_SUCCESS IFF we can retrieve the specified entry.
+ */
+int wt_entry_get(
+       Operation *op,
+       struct berval *ndn,
+       ObjectClass *oc,
+       AttributeDescription *at,
+       int rw,
+       Entry **ent )
+{
+       return 0;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/idl.c b/servers/slapd/back-wt/idl.c
new file mode 100644 (file)
index 0000000..6548e94
--- /dev/null
@@ -0,0 +1,794 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "idl.h"
+
+#define IDL_MAX(x,y)   ( (x) > (y) ? (x) : (y) )
+#define IDL_MIN(x,y)   ( (x) < (y) ? (x) : (y) )
+#define IDL_CMP(x,y)   ( (x) < (y) ? -1 : (x) > (y) )
+
+#if IDL_DEBUG > 0
+static void idl_check( ID *ids )
+{
+       if( WT_IDL_IS_RANGE( ids ) ) {
+               assert( WT_IDL_RANGE_FIRST(ids) <= WT_IDL_RANGE_LAST(ids) );
+       } else {
+               ID i;
+               for( i=1; i < ids[0]; i++ ) {
+                       assert( ids[i+1] > ids[i] );
+               }
+       }
+}
+
+#if IDL_DEBUG > 1
+static void idl_dump( ID *ids )
+{
+       if( WT_IDL_IS_RANGE( ids ) ) {
+               Debug( LDAP_DEBUG_ANY,
+               "IDL: range ( %ld - %ld )\n",
+               (long) WT_IDL_RANGE_FIRST( ids ),
+               (long) WT_IDL_RANGE_LAST( ids ),
+               0);
+
+       } else {
+               ID i;
+               Debug( LDAP_DEBUG_ANY, "IDL: size %ld", (long) ids[0], 0, 0 );
+
+               for( i=1; i<=ids[0]; i++ ) {
+                       if( i % 16 == 1 ) {
+                               Debug( LDAP_DEBUG_ANY, "\n", 0, 0, 0 );
+                       }
+                       Debug( LDAP_DEBUG_ANY, "  %02lx", (long) ids[i], 0, 0 );
+               }
+
+               Debug( LDAP_DEBUG_ANY, "\n", 0, 0, 0 );
+       }
+
+       idl_check( ids );
+}
+#endif /* IDL_DEBUG > 1 */
+#endif /* IDL_DEBUG > 0 */
+
+unsigned wt_idl_search( ID *ids, ID id )
+{
+#define IDL_BINARY_SEARCH 1
+#ifdef IDL_BINARY_SEARCH
+       /*
+        * binary search of id in ids
+        * if found, returns position of id
+        * if not found, returns first postion greater than id
+        */
+       unsigned base = 0;
+       unsigned cursor = 1;
+       int val = 0;
+       unsigned n = ids[0];
+
+#if IDL_DEBUG > 0
+       idl_check( ids );
+#endif
+
+       while( 0 < n ) {
+               unsigned pivot = n >> 1;
+               cursor = base + pivot + 1;
+               val = IDL_CMP( id, ids[cursor] );
+
+               if( val < 0 ) {
+                       n = pivot;
+
+               } else if ( val > 0 ) {
+                       base = cursor;
+                       n -= pivot + 1;
+
+               } else {
+                       return cursor;
+               }
+       }
+
+       if( val > 0 ) {
+               ++cursor;
+       }
+       return cursor;
+
+#else
+       /* (reverse) linear search */
+       int i;
+
+#if IDL_DEBUG > 0
+       idl_check( ids );
+#endif
+
+       for( i=ids[0]; i; i-- ) {
+               if( id > ids[i] ) {
+                       break;
+               }
+       }
+
+       return i+1;
+#endif
+}
+
+int wt_idl_insert( ID *ids, ID id )
+{
+       unsigned x;
+
+#if IDL_DEBUG > 1
+       Debug( LDAP_DEBUG_ANY, "insert: %04lx at %d\n", (long) id, x, 0 );
+       idl_dump( ids );
+#elif IDL_DEBUG > 0
+       idl_check( ids );
+#endif
+
+       if (WT_IDL_IS_RANGE( ids )) {
+               /* if already in range, treat as a dup */
+               if (id >= WT_IDL_RANGE_FIRST(ids) && id <= WT_IDL_RANGE_LAST(ids))
+                       return -1;
+               if (id < WT_IDL_RANGE_FIRST(ids))
+                       ids[1] = id;
+               else if (id > WT_IDL_RANGE_LAST(ids))
+                       ids[2] = id;
+               return 0;
+       }
+
+       x = wt_idl_search( ids, id );
+       assert( x > 0 );
+
+       if( x < 1 ) {
+               /* internal error */
+               return -2;
+       }
+
+       if ( x <= ids[0] && ids[x] == id ) {
+               /* duplicate */
+               return -1;
+       }
+
+       if ( ++ids[0] >= WT_IDL_DB_MAX ) {
+               if( id < ids[1] ) {
+                       ids[1] = id;
+                       ids[2] = ids[ids[0]-1];
+               } else if ( ids[ids[0]-1] < id ) {
+                       ids[2] = id;
+               } else {
+                       ids[2] = ids[ids[0]-1];
+               }
+               ids[0] = NOID;
+
+       } else {
+               /* insert id */
+               AC_MEMCPY( &ids[x+1], &ids[x], (ids[0]-x) * sizeof(ID) );
+               ids[x] = id;
+       }
+
+#if IDL_DEBUG > 1
+       idl_dump( ids );
+#elif IDL_DEBUG > 0
+       idl_check( ids );
+#endif
+
+       return 0;
+}
+
+static int wt_idl_delete( ID *ids, ID id )
+{
+       unsigned x;
+
+#if IDL_DEBUG > 1
+       Debug( LDAP_DEBUG_ANY, "delete: %04lx at %d\n", (long) id, x, 0 );
+       idl_dump( ids );
+#elif IDL_DEBUG > 0
+       idl_check( ids );
+#endif
+
+       if (WT_IDL_IS_RANGE( ids )) {
+               /* If deleting a range boundary, adjust */
+               if ( ids[1] == id )
+                       ids[1]++;
+               else if ( ids[2] == id )
+                       ids[2]--;
+               /* deleting from inside a range is a no-op */
+
+               /* If the range has collapsed, re-adjust */
+               if ( ids[1] > ids[2] )
+                       ids[0] = 0;
+               else if ( ids[1] == ids[2] )
+                       ids[1] = 1;
+               return 0;
+       }
+
+       x = wt_idl_search( ids, id );
+       assert( x > 0 );
+
+       if( x <= 0 ) {
+               /* internal error */
+               return -2;
+       }
+
+       if( x > ids[0] || ids[x] != id ) {
+               /* not found */
+               return -1;
+
+       } else if ( --ids[0] == 0 ) {
+               if( x != 1 ) {
+                       return -3;
+               }
+
+       } else {
+               AC_MEMCPY( &ids[x], &ids[x+1], (1+ids[0]-x) * sizeof(ID) );
+       }
+
+#if IDL_DEBUG > 1
+       idl_dump( ids );
+#elif IDL_DEBUG > 0
+       idl_check( ids );
+#endif
+
+       return 0;
+}
+
+static char *
+wt_show_key(
+       char            *buf,
+       void            *val,
+       size_t          len )
+{
+       if ( len == 4 /* LUTIL_HASH_BYTES */ ) {
+               unsigned char *c = val;
+               sprintf( buf, "[%02x%02x%02x%02x]", c[0], c[1], c[2], c[3] );
+               return buf;
+       } else {
+               return val;
+       }
+}
+
+/*
+ * idl_intersection - return a = a intersection b
+ */
+int
+wt_idl_intersection(
+       ID *a,
+       ID *b )
+{
+       ID ida, idb;
+       ID idmax, idmin;
+       ID cursora = 0, cursorb = 0, cursorc;
+       int swap = 0;
+
+       if ( WT_IDL_IS_ZERO( a ) || WT_IDL_IS_ZERO( b ) ) {
+               a[0] = 0;
+               return 0;
+       }
+
+       idmin = IDL_MAX( WT_IDL_FIRST(a), WT_IDL_FIRST(b) );
+       idmax = IDL_MIN( WT_IDL_LAST(a), WT_IDL_LAST(b) );
+       if ( idmin > idmax ) {
+               a[0] = 0;
+               return 0;
+       } else if ( idmin == idmax ) {
+               a[0] = 1;
+               a[1] = idmin;
+               return 0;
+       }
+
+       if ( WT_IDL_IS_RANGE( a ) ) {
+               if ( WT_IDL_IS_RANGE(b) ) {
+               /* If both are ranges, just shrink the boundaries */
+                       a[1] = idmin;
+                       a[2] = idmax;
+                       return 0;
+               } else {
+               /* Else swap so that b is the range, a is a list */
+                       ID *tmp = a;
+                       a = b;
+                       b = tmp;
+                       swap = 1;
+               }
+       }
+
+       /* If a range completely covers the list, the result is
+        * just the list. If idmin to idmax is contiguous, just
+        * turn it into a range.
+        */
+       if ( WT_IDL_IS_RANGE( b )
+               && WT_IDL_RANGE_FIRST( b ) <= WT_IDL_FIRST( a )
+               && WT_IDL_RANGE_LAST( b ) >= WT_IDL_LLAST( a ) ) {
+               if (idmax - idmin + 1 == a[0])
+               {
+                       a[0] = NOID;
+                       a[1] = idmin;
+                       a[2] = idmax;
+               }
+               goto done;
+       }
+
+       /* Fine, do the intersection one element at a time.
+        * First advance to idmin in both IDLs.
+        */
+       cursora = cursorb = idmin;
+       ida = wt_idl_first( a, &cursora );
+       idb = wt_idl_first( b, &cursorb );
+       cursorc = 0;
+
+       while( ida <= idmax || idb <= idmax ) {
+               if( ida == idb ) {
+                       a[++cursorc] = ida;
+                       ida = wt_idl_next( a, &cursora );
+                       idb = wt_idl_next( b, &cursorb );
+               } else if ( ida < idb ) {
+                       ida = wt_idl_next( a, &cursora );
+               } else {
+                       idb = wt_idl_next( b, &cursorb );
+               }
+       }
+       a[0] = cursorc;
+done:
+       if (swap)
+               WT_IDL_CPY( b, a );
+
+       return 0;
+}
+
+
+/*
+ * idl_union - return a = a union b
+ */
+int
+wt_idl_union(
+       ID      *a,
+       ID      *b )
+{
+       ID ida, idb;
+       ID cursora = 0, cursorb = 0, cursorc;
+
+       if ( WT_IDL_IS_ZERO( b ) ) {
+               return 0;
+       }
+
+       if ( WT_IDL_IS_ZERO( a ) ) {
+               WT_IDL_CPY( a, b );
+               return 0;
+       }
+
+       if ( WT_IDL_IS_RANGE( a ) || WT_IDL_IS_RANGE(b) ) {
+over:          ida = IDL_MIN( WT_IDL_FIRST(a), WT_IDL_FIRST(b) );
+               idb = IDL_MAX( WT_IDL_LAST(a), WT_IDL_LAST(b) );
+               a[0] = NOID;
+               a[1] = ida;
+               a[2] = idb;
+               return 0;
+       }
+
+       ida = wt_idl_first( a, &cursora );
+       idb = wt_idl_first( b, &cursorb );
+
+       cursorc = b[0];
+
+       /* The distinct elements of a are cat'd to b */
+       while( ida != NOID || idb != NOID ) {
+               if ( ida < idb ) {
+                       if( ++cursorc > WT_IDL_UM_MAX ) {
+                               goto over;
+                       }
+                       b[cursorc] = ida;
+                       ida = wt_idl_next( a, &cursora );
+
+               } else {
+                       if ( ida == idb )
+                               ida = wt_idl_next( a, &cursora );
+                       idb = wt_idl_next( b, &cursorb );
+               }
+       }
+
+       /* b is copied back to a in sorted order */
+       a[0] = cursorc;
+       cursora = 1;
+       cursorb = 1;
+       cursorc = b[0]+1;
+       while (cursorb <= b[0] || cursorc <= a[0]) {
+               if (cursorc > a[0])
+                       idb = NOID;
+               else
+                       idb = b[cursorc];
+               if (cursorb <= b[0] && b[cursorb] < idb)
+                       a[cursora++] = b[cursorb++];
+               else {
+                       a[cursora++] = idb;
+                       cursorc++;
+               }
+       }
+
+       return 0;
+}
+
+
+#if 0
+/*
+ * wt_idl_notin - return a intersection ~b (or a minus b)
+ */
+int
+wt_idl_notin(
+       ID      *a,
+       ID      *b,
+       ID *ids )
+{
+       ID ida, idb;
+       ID cursora = 0, cursorb = 0;
+
+       if( WT_IDL_IS_ZERO( a ) ||
+               WT_IDL_IS_ZERO( b ) ||
+               WT_IDL_IS_RANGE( b ) )
+       {
+               WT_IDL_CPY( ids, a );
+               return 0;
+       }
+
+       if( WT_IDL_IS_RANGE( a ) ) {
+               WT_IDL_CPY( ids, a );
+               return 0;
+       }
+
+       ida = wt_idl_first( a, &cursora ),
+       idb = wt_idl_first( b, &cursorb );
+
+       ids[0] = 0;
+
+       while( ida != NOID ) {
+               if ( idb == NOID ) {
+                       /* we could shortcut this */
+                       ids[++ids[0]] = ida;
+                       ida = wt_idl_next( a, &cursora );
+
+               } else if ( ida < idb ) {
+                       ids[++ids[0]] = ida;
+                       ida = wt_idl_next( a, &cursora );
+
+               } else if ( ida > idb ) {
+                       idb = wt_idl_next( b, &cursorb );
+
+               } else {
+                       ida = wt_idl_next( a, &cursora );
+                       idb = wt_idl_next( b, &cursorb );
+               }
+       }
+
+       return 0;
+}
+#endif
+
+ID wt_idl_first( ID *ids, ID *cursor )
+{
+       ID pos;
+
+       if ( ids[0] == 0 ) {
+               *cursor = NOID;
+               return NOID;
+       }
+
+       if ( WT_IDL_IS_RANGE( ids ) ) {
+               if( *cursor < ids[1] ) {
+                       *cursor = ids[1];
+               }
+               return *cursor;
+       }
+
+       if ( *cursor == 0 )
+               pos = 1;
+       else
+               pos = wt_idl_search( ids, *cursor );
+
+       if( pos > ids[0] ) {
+               return NOID;
+       }
+
+       *cursor = pos;
+       return ids[pos];
+}
+
+ID wt_idl_next( ID *ids, ID *cursor )
+{
+       if ( WT_IDL_IS_RANGE( ids ) ) {
+               if( ids[2] < ++(*cursor) ) {
+                       return NOID;
+               }
+               return *cursor;
+       }
+
+       if ( ++(*cursor) <= ids[0] ) {
+               return ids[*cursor];
+       }
+
+       return NOID;
+}
+
+/* Add one ID to an unsorted list. We ensure that the first element is the
+ * minimum and the last element is the maximum, for fast range compaction.
+ *   this means IDLs up to length 3 are always sorted...
+ */
+int wt_idl_append_one( ID *ids, ID id )
+{
+       if (WT_IDL_IS_RANGE( ids )) {
+               /* if already in range, treat as a dup */
+               if (id >= WT_IDL_RANGE_FIRST(ids) && id <= WT_IDL_RANGE_LAST(ids))
+                       return -1;
+               if (id < WT_IDL_RANGE_FIRST(ids))
+                       ids[1] = id;
+               else if (id > WT_IDL_RANGE_LAST(ids))
+                       ids[2] = id;
+               return 0;
+       }
+       if ( ids[0] ) {
+               ID tmp;
+
+               if (id < ids[1]) {
+                       tmp = ids[1];
+                       ids[1] = id;
+                       id = tmp;
+               }
+               if ( ids[0] > 1 && id < ids[ids[0]] ) {
+                       tmp = ids[ids[0]];
+                       ids[ids[0]] = id;
+                       id = tmp;
+               }
+       }
+       ids[0]++;
+       if ( ids[0] >= WT_IDL_UM_MAX ) {
+               ids[0] = NOID;
+               ids[2] = id;
+       } else {
+               ids[ids[0]] = id;
+       }
+       return 0;
+}
+
+/* Append sorted list b to sorted list a. The result is unsorted but
+ * a[1] is the min of the result and a[a[0]] is the max.
+ */
+int wt_idl_append( ID *a, ID *b )
+{
+       ID ida, idb, tmp, swap = 0;
+
+       if ( WT_IDL_IS_ZERO( b ) ) {
+               return 0;
+       }
+
+       if ( WT_IDL_IS_ZERO( a ) ) {
+               WT_IDL_CPY( a, b );
+               return 0;
+       }
+
+       ida = WT_IDL_LAST( a );
+       idb = WT_IDL_LAST( b );
+       if ( WT_IDL_IS_RANGE( a ) || WT_IDL_IS_RANGE(b) ||
+               a[0] + b[0] >= WT_IDL_UM_MAX ) {
+               a[2] = IDL_MAX( ida, idb );
+               a[1] = IDL_MIN( a[1], b[1] );
+               a[0] = NOID;
+               return 0;
+       }
+
+       if ( b[0] > 1 && ida > idb ) {
+               swap = idb;
+               a[a[0]] = idb;
+               b[b[0]] = ida;
+       }
+
+       if ( b[1] < a[1] ) {
+               tmp = a[1];
+               a[1] = b[1];
+       } else {
+               tmp = b[1];
+       }
+       a[0]++;
+       a[a[0]] = tmp;
+
+       if ( b[0] > 1 ) {
+               int i = b[0] - 1;
+               AC_MEMCPY(a+a[0]+1, b+2, i * sizeof(ID));
+               a[0] += i;
+       }
+       if ( swap ) {
+               b[b[0]] = swap;
+       }
+       return 0;
+}
+
+#if 1
+
+/* Quicksort + Insertion sort for small arrays */
+
+#define SMALL  8
+#define        SWAP(a,b)       itmp=(a);(a)=(b);(b)=itmp
+
+void
+wt_idl_sort( ID *ids, ID *tmp )
+{
+       int *istack = (int *)tmp; /* Private stack, not used by caller */
+       int i,j,k,l,ir,jstack;
+       ID a, itmp;
+
+       if ( WT_IDL_IS_RANGE( ids ))
+               return;
+
+       ir = ids[0];
+       l = 1;
+       jstack = 0;
+       for(;;) {
+               if (ir - l < SMALL) {   /* Insertion sort */
+                       for (j=l+1;j<=ir;j++) {
+                               a = ids[j];
+                               for (i=j-1;i>=1;i--) {
+                                       if (ids[i] <= a) break;
+                                       ids[i+1] = ids[i];
+                               }
+                               ids[i+1] = a;
+                       }
+                       if (jstack == 0) break;
+                       ir = istack[jstack--];
+                       l = istack[jstack--];
+               } else {
+                       k = (l + ir) >> 1;      /* Choose median of left, center, right */
+                       SWAP(ids[k], ids[l+1]);
+                       if (ids[l] > ids[ir]) {
+                               SWAP(ids[l], ids[ir]);
+                       }
+                       if (ids[l+1] > ids[ir]) {
+                               SWAP(ids[l+1], ids[ir]);
+                       }
+                       if (ids[l] > ids[l+1]) {
+                               SWAP(ids[l], ids[l+1]);
+                       }
+                       i = l+1;
+                       j = ir;
+                       a = ids[l+1];
+                       for(;;) {
+                               do i++; while(ids[i] < a);
+                               do j--; while(ids[j] > a);
+                               if (j < i) break;
+                               SWAP(ids[i],ids[j]);
+                       }
+                       ids[l+1] = ids[j];
+                       ids[j] = a;
+                       jstack += 2;
+                       if (ir-i+1 >= j-l) {
+                               istack[jstack] = ir;
+                               istack[jstack-1] = i;
+                               ir = j-1;
+                       } else {
+                               istack[jstack] = j-1;
+                               istack[jstack-1] = l;
+                               l = i;
+                       }
+               }
+       }
+}
+
+#else
+
+/* 8 bit Radix sort + insertion sort
+ *
+ * based on code from http://www.cubic.org/docs/radix.htm
+ * with improvements by ebackes@symas.com and hyc@symas.com
+ *
+ * This code is O(n) but has a relatively high constant factor. For lists
+ * up to ~50 Quicksort is slightly faster; up to ~100 they are even.
+ * Much faster than quicksort for lists longer than ~100. Insertion
+ * sort is actually superior for lists <50.
+ */
+
+#define BUCKETS        (1<<8)
+#define SMALL  50
+
+void
+wt_idl_sort( ID *ids, ID *tmp )
+{
+       int count, soft_limit, phase = 0, size = ids[0];
+       ID *idls[2];
+       unsigned char *maxv = (unsigned char *)&ids[size];
+
+       if ( WT_IDL_IS_RANGE( ids ))
+               return;
+
+       /* Use insertion sort for small lists */
+       if ( size <= SMALL ) {
+               int i,j;
+               ID a;
+
+               for (j=1;j<=size;j++) {
+                       a = ids[j];
+                       for (i=j-1;i>=1;i--) {
+                               if (ids[i] <= a) break;
+                               ids[i+1] = ids[i];
+                       }
+                       ids[i+1] = a;
+               }
+               return;
+       }
+
+       tmp[0] = size;
+       idls[0] = ids;
+       idls[1] = tmp;
+
+#if BYTE_ORDER == BIG_ENDIAN
+    for (soft_limit = 0; !maxv[soft_limit]; soft_limit++);
+#else
+    for (soft_limit = sizeof(ID)-1; !maxv[soft_limit]; soft_limit--);
+#endif
+
+       for (
+#if BYTE_ORDER == BIG_ENDIAN
+       count = sizeof(ID)-1; count >= soft_limit; --count
+#else
+       count = 0; count <= soft_limit; ++count
+#endif
+       ) {
+               unsigned int num[BUCKETS], * np, n, sum;
+               int i;
+        ID *sp, *source, *dest;
+        unsigned char *bp, *source_start;
+
+               source = idls[phase]+1;
+               dest = idls[phase^1]+1;
+               source_start =  ((unsigned char *) source) + count;
+
+        np = num;
+        for ( i = BUCKETS; i > 0; --i ) *np++ = 0;
+
+               /* count occurences of every byte value */
+               bp = source_start;
+        for ( i = size; i > 0; --i, bp += sizeof(ID) )
+                               num[*bp]++;
+
+               /* transform count into index by summing elements and storing
+                * into same array
+                */
+        sum = 0;
+        np = num;
+        for ( i = BUCKETS; i > 0; --i ) {
+                n = *np;
+                *np++ = sum;
+                sum += n;
+        }
+
+               /* fill dest with the right values in the right place */
+               bp = source_start;
+        sp = source;
+        for ( i = size; i > 0; --i, bp += sizeof(ID) ) {
+                np = num + *bp;
+                dest[*np] = *sp++;
+                ++(*np);
+        }
+               phase ^= 1;
+       }
+
+       /* copy back from temp if needed */
+       if ( phase ) {
+               ids++; tmp++;
+               for ( count = 0; count < size; ++count )
+                       *ids++ = *tmp++;
+       }
+}
+#endif /* Quick vs Radix */
+
diff --git a/servers/slapd/back-wt/idl.h b/servers/slapd/back-wt/idl.h
new file mode 100644 (file)
index 0000000..52948a8
--- /dev/null
@@ -0,0 +1,80 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#ifndef _WI_IDL_H_
+#define _WT_IDL_H_
+
+/* IDL sizes - likely should be even bigger
+ *   limiting factors: sizeof(ID), thread stack size
+ */
+#define        WT_IDL_LOGN     16      /* DB_SIZE is 2^16, UM_SIZE is 2^17 */
+#define WT_IDL_DB_SIZE         (1<<WT_IDL_LOGN)
+#define WT_IDL_UM_SIZE         (1<<(WT_IDL_LOGN+1))
+#define WT_IDL_UM_SIZEOF       (WT_IDL_UM_SIZE * sizeof(ID))
+
+#define WT_IDL_DB_MAX          (WT_IDL_DB_SIZE-1)
+
+#define WT_IDL_UM_MAX          (WT_IDL_UM_SIZE-1)
+
+#define WT_IDL_IS_RANGE(ids)   ((ids)[0] == NOID)
+#define WT_IDL_RANGE_SIZE              (3)
+#define WT_IDL_RANGE_SIZEOF    (WT_IDL_RANGE_SIZE * sizeof(ID))
+#define WT_IDL_SIZEOF(ids)             ((WT_IDL_IS_RANGE(ids) \
+       ? WT_IDL_RANGE_SIZE : ((ids)[0]+1)) * sizeof(ID))
+
+#define WT_IDL_RANGE_FIRST(ids)        ((ids)[1])
+#define WT_IDL_RANGE_LAST(ids)         ((ids)[2])
+
+#define WT_IDL_RANGE( ids, f, l ) \
+       do { \
+               (ids)[0] = NOID; \
+               (ids)[1] = (f);  \
+               (ids)[2] = (l);  \
+       } while(0)
+
+#define WT_IDL_ZERO(ids) \
+       do { \
+               (ids)[0] = 0; \
+               (ids)[1] = 0; \
+               (ids)[2] = 0; \
+       } while(0)
+
+#define WT_IDL_IS_ZERO(ids) ( (ids)[0] == 0 )
+#define WT_IDL_IS_ALL( range, ids ) ( (ids)[0] == NOID \
+       && (ids)[1] <= (range)[1] && (range)[2] <= (ids)[2] )
+
+#define WT_IDL_CPY( dst, src ) (AC_MEMCPY( dst, src, WT_IDL_SIZEOF( src ) ))
+
+#define WT_IDL_ID( wi, ids, id ) WT_IDL_RANGE( ids, id, ((wi)->wi_lastid) )
+#define WT_IDL_ALL( wi, ids ) WT_IDL_RANGE( ids, 1, ((wi)->wi_lastid) )
+
+#define WT_IDL_FIRST( ids )    ( (ids)[1] )
+#define WT_IDL_LLAST( ids )    ( (ids)[(ids)[0]] )
+#define WT_IDL_LAST( ids )             ( WT_IDL_IS_RANGE(ids) \
+       ? (ids)[2] : (ids)[(ids)[0]] )
+
+#define WT_IDL_N( ids )                ( WT_IDL_IS_RANGE(ids) \
+       ? ((ids)[2]-(ids)[1])+1 : (ids)[0] )
+
+LDAP_BEGIN_DECL
+LDAP_END_DECL
+
+#endif
diff --git a/servers/slapd/back-wt/index.c b/servers/slapd/back-wt/index.c
new file mode 100644 (file)
index 0000000..812257a
--- /dev/null
@@ -0,0 +1,391 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include "back-wt.h"
+#include "config.h"
+
+static char presence_keyval[] = {0,0};
+static struct berval presence_key = BER_BVC(presence_keyval);
+
+AttrInfo *wt_index_mask(
+       Backend *be,
+       AttributeDescription *desc,
+       struct berval *atname )
+{
+       AttributeType *at;
+       AttrInfo *ai = wt_attr_mask( be->be_private, desc );
+
+       if( ai ) {
+               *atname = desc->ad_cname;
+               return ai;
+       }
+
+       /* If there is a tagging option, did we ever index the base
+        * type? If so, check for mask, otherwise it's not there.
+        */
+       if( slap_ad_is_tagged( desc ) && desc != desc->ad_type->sat_ad ) {
+               /* has tagging option */
+               ai = wt_attr_mask( be->be_private, desc->ad_type->sat_ad );
+
+               if ( ai && !( ai->ai_indexmask & SLAP_INDEX_NOTAGS ) ) {
+                       *atname = desc->ad_type->sat_cname;
+                       return ai;
+               }
+       }
+
+       /* see if supertype defined mask for its subtypes */
+       for( at = desc->ad_type; at != NULL ; at = at->sat_sup ) {
+               /* If no AD, we've never indexed this type */
+               if ( !at->sat_ad ) continue;
+
+               ai = wt_attr_mask( be->be_private, at->sat_ad );
+
+               if ( ai && !( ai->ai_indexmask & SLAP_INDEX_NOSUBTYPES ) ) {
+                       *atname = at->sat_cname;
+                       return ai;
+               }
+       }
+
+       return 0;
+}
+
+/* This function is only called when evaluating search filters.
+ */
+int wt_index_param(
+       Backend *be,
+       AttributeDescription *desc,
+       int ftype,
+       slap_mask_t *maskp,
+       struct berval *prefixp )
+{
+       AttrInfo *ai;
+       int rc;
+       slap_mask_t mask, type = 0;
+
+       ai = wt_index_mask( be, desc, prefixp );
+
+       if ( !ai ) {
+               /* TODO: add monitor */
+               return LDAP_INAPPROPRIATE_MATCHING;
+       }
+       mask = ai->ai_indexmask;
+
+       switch( ftype ) {
+       case LDAP_FILTER_PRESENT:
+               type = SLAP_INDEX_PRESENT;
+               if( IS_SLAP_INDEX( mask, SLAP_INDEX_PRESENT ) ) {
+                       *prefixp = presence_key;
+                       *maskp = mask;
+                       return LDAP_SUCCESS;
+               }
+               break;
+
+       case LDAP_FILTER_APPROX:
+               type = SLAP_INDEX_APPROX;
+               if ( desc->ad_type->sat_approx ) {
+                       if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) ) {
+                               *maskp = mask;
+                               return LDAP_SUCCESS;
+                       }
+                       break;
+               }
+
+               /* Use EQUALITY rule and index for approximate match */
+               /* fall thru */
+
+       case LDAP_FILTER_EQUALITY:
+               type = SLAP_INDEX_EQUALITY;
+               if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) ) {
+                       *maskp = mask;
+                       return LDAP_SUCCESS;
+               }
+               break;
+
+       case LDAP_FILTER_SUBSTRINGS:
+               type = SLAP_INDEX_SUBSTR;
+               if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) ) {
+                       *maskp = mask;
+                       return LDAP_SUCCESS;
+               }
+               break;
+
+       default:
+               return LDAP_OTHER;
+       }
+
+       /* TODO: add monitor index */
+       return LDAP_INAPPROPRIATE_MATCHING;
+}
+
+static int indexer(
+       Operation *op,
+       wt_ctx *wc,
+       AttributeDescription *ad,
+       struct berval *atname,
+       BerVarray vals,
+       ID id,
+       int opid,
+       slap_mask_t mask )
+{
+       int rc, i;
+       struct berval *keys;
+       WT_CURSOR *cursor = NULL;
+       WT_SESSION *session = wc->session;
+       assert( mask != 0 );
+
+       cursor = wt_ctx_index_cursor(wc, atname, 1);
+       if( !cursor ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(indexer)
+                          ": open index cursor failed: %s\n",
+                          atname->bv_val, 0, 0 );
+               goto done;
+       }
+
+       if( IS_SLAP_INDEX( mask, SLAP_INDEX_PRESENT ) ) {
+               rc = wt_key_change( op->o_bd, cursor, &presence_key, id, opid );
+               if( rc ) {
+                       goto done;
+               }
+       }
+
+       if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) ) {
+               rc = ad->ad_type->sat_equality->smr_indexer(
+                       LDAP_FILTER_EQUALITY,
+                       mask,
+                       ad->ad_type->sat_syntax,
+                       ad->ad_type->sat_equality,
+                       atname, vals, &keys, op->o_tmpmemctx );
+
+               if( rc == LDAP_SUCCESS && keys != NULL ) {
+                       for( i=0; keys[i].bv_val != NULL; i++ ) {
+                               rc = wt_key_change( op->o_bd, cursor, &keys[i], id, opid );
+                               if( rc ) {
+                                       ber_bvarray_free_x( keys, op->o_tmpmemctx );
+                                       goto done;
+                               }
+                       }
+                       ber_bvarray_free_x( keys, op->o_tmpmemctx );
+               }
+               rc = LDAP_SUCCESS;
+       }
+
+       if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) ) {
+               rc = ad->ad_type->sat_approx->smr_indexer(
+                       LDAP_FILTER_APPROX,
+                       mask,
+                       ad->ad_type->sat_syntax,
+                       ad->ad_type->sat_approx,
+                       atname, vals, &keys, op->o_tmpmemctx );
+
+               if( rc == LDAP_SUCCESS && keys != NULL ) {
+                       for( i=0; keys[i].bv_val != NULL; i++ ) {
+                               rc = wt_key_change( op->o_bd, cursor, &keys[i], id, opid );
+                               if( rc ) {
+                                       ber_bvarray_free_x( keys, op->o_tmpmemctx );
+                                       goto done;
+                               }
+                       }
+                       ber_bvarray_free_x( keys, op->o_tmpmemctx );
+               }
+
+               rc = LDAP_SUCCESS;
+       }
+
+       if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) ) {
+               rc = ad->ad_type->sat_substr->smr_indexer(
+                       LDAP_FILTER_SUBSTRINGS,
+                       mask,
+                       ad->ad_type->sat_syntax,
+                       ad->ad_type->sat_substr,
+                       atname, vals, &keys, op->o_tmpmemctx );
+
+               if( rc == LDAP_SUCCESS && keys != NULL ) {
+                       for( i=0; keys[i].bv_val != NULL; i++ ) {
+                               rc = wt_key_change( op->o_bd, cursor, &keys[i], id, opid );
+                               if( rc ) {
+                                       ber_bvarray_free_x( keys, op->o_tmpmemctx );
+                                       goto done;
+                               }
+                       }
+                       ber_bvarray_free_x( keys, op->o_tmpmemctx );
+               }
+
+               rc = LDAP_SUCCESS;
+       }
+
+done:
+       if(cursor){
+               cursor->close(cursor);
+       }
+       return rc;
+}
+
+static int index_at_values(
+       Operation *op,
+       wt_ctx *wc,
+       AttributeDescription *ad,
+       AttributeType *type,
+       struct berval *tags,
+       BerVarray vals,
+       ID id,
+       int opid )
+{
+       int rc;
+       slap_mask_t mask = 0;
+       int ixop = opid;
+       AttrInfo *ai = NULL;
+
+       if ( opid == WT_INDEX_UPDATE_OP )
+               ixop = SLAP_INDEX_ADD_OP;
+
+       if( type->sat_sup ) {
+               /* recurse */
+               rc = index_at_values( op, wc, NULL,
+                                                         type->sat_sup, tags,
+                                                         vals, id, opid );
+
+               if( rc ) return rc;
+       }
+
+       /* If this type has no AD, we've never used it before */
+       if( type->sat_ad ) {
+               ai = wt_attr_mask( op->o_bd->be_private, type->sat_ad );
+               if ( ai ) {
+                       #ifdef LDAP_COMP_MATCH
+                       /* component indexing */
+                       if ( ai->ai_cr ) {
+                               ComponentReference *cr;
+                               for( cr = ai->ai_cr ; cr ; cr = cr->cr_next ) {
+                                       rc = indexer( op, wc, cr->cr_ad, &type->sat_cname,
+                                                                 cr->cr_nvals, id, ixop,
+                                                                 cr->cr_indexmask );
+                               }
+                       }
+                       #endif
+                       ad = type->sat_ad;
+                       /* If we're updating the index, just set the new bits that aren't
+             * already in the old mask.
+             */
+                       if ( opid == WT_INDEX_UPDATE_OP )
+                               mask = ai->ai_newmask & ~ai->ai_indexmask;
+                       else
+                               /* For regular updates, if there is a newmask use it. Otherwise
+                                * just use the old mask.
+                                */
+                               mask = ai->ai_newmask ? ai->ai_newmask : ai->ai_indexmask;
+                       if( mask ) {
+                               rc = indexer( op, wc, ad, &type->sat_cname,
+                                                         vals, id, ixop, mask );
+                               if( rc ) return rc;
+                       }
+               }
+       }
+
+       if( tags->bv_len ) {
+               AttributeDescription *desc;
+
+               desc = ad_find_tags( type, tags );
+               if( desc ) {
+                       ai = wt_attr_mask( op->o_bd->be_private, desc );
+
+                       if( ai ) {
+                               if ( opid == WT_INDEX_UPDATE_OP )
+                                       mask = ai->ai_newmask & ~ai->ai_indexmask;
+                               else
+                                       mask = ai->ai_newmask ? ai->ai_newmask : ai->ai_indexmask;
+                               if ( mask ) {
+                                       rc = indexer( op, wc, desc, &desc->ad_cname,
+                                                                 vals, id, ixop, mask );
+
+                                       if( rc ) {
+                                               return rc;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       return LDAP_SUCCESS;
+}
+
+int wt_index_values(
+       Operation *op,
+       wt_ctx *wc,
+       AttributeDescription *desc,
+       BerVarray vals,
+       ID id,
+       int opid )
+{
+       int rc;
+
+       /* Never index ID 0 */
+       if ( id == 0 )
+               return 0;
+
+       rc = index_at_values( op, wc, desc,
+                                                 desc->ad_type, &desc->ad_tags,
+                                                 vals, id, opid );
+
+       return rc;
+}
+
+int
+wt_index_entry( Operation *op, wt_ctx *wc, int opid, Entry *e )
+{
+       int rc;
+       Attribute *ap = e->e_attrs;
+
+       if ( e->e_id == 0 )
+               return 0;
+
+       Debug( LDAP_DEBUG_TRACE, "=> index_entry_%s( %ld, \"%s\" )\n",
+                  opid == SLAP_INDEX_DELETE_OP ? "del" : "add",
+                  (long) e->e_id, e->e_dn ? e->e_dn : "" );
+
+       for ( ; ap != NULL; ap = ap->a_next ) {
+               rc = wt_index_values( op, wc, ap->a_desc,
+                                                         ap->a_nvals, e->e_id, opid );
+               if( rc != LDAP_SUCCESS ) {
+                       Debug( LDAP_DEBUG_TRACE,
+                                  "<= index_entry_%s( %ld, \"%s\" ) failure\n",
+                                  opid == SLAP_INDEX_ADD_OP ? "add" : "del",
+                                  (long) e->e_id, e->e_dn );
+                       return rc;
+               }
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "<= index_entry_%s( %ld, \"%s\" ) success\n",
+                  opid == SLAP_INDEX_DELETE_OP ? "del" : "add",
+                  (long) e->e_id, e->e_dn ? e->e_dn : "" );
+       return 0;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/init.c b/servers/slapd/back-wt/init.c
new file mode 100644 (file)
index 0000000..1623532
--- /dev/null
@@ -0,0 +1,308 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "config.h"
+
+static int
+wt_db_init( BackendDB *be, ConfigReply *cr )
+{
+       struct wt_info *wi;
+
+       Debug( LDAP_DEBUG_TRACE,
+                  LDAP_XSTRING(wt_db_init) ": Initializing wt backend\n",
+                  0, 0, 0 );
+
+       /* allocate backend-database-specific stuff */
+    wi = ch_calloc( 1, sizeof(struct wt_info) );
+
+       wi->wi_dbenv_home = ch_strdup( SLAPD_DEFAULT_DB_DIR );
+       wi->wi_dbenv_config = ch_strdup("create");
+       wi->wi_lastid = 0;
+       wi->wi_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
+       wi->wi_search_stack = NULL;
+
+       be->be_private = wi;
+       be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+       return LDAP_SUCCESS;
+}
+
+static int
+wt_db_open( BackendDB *be, ConfigReply *cr )
+{
+       struct wt_info *wi = (struct wt_info *) be->be_private;
+       int rc;
+       struct stat st;
+       WT_CONNECTION *conn;
+       WT_SESSION *session;
+
+       if ( be->be_suffix == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_db_open) ": need suffix.\n",
+                          1, 0, 0 );
+               return -1;
+       }
+
+       Debug( LDAP_DEBUG_ARGS,
+                  LDAP_XSTRING(wt_db_open) ": \"%s\"\n",
+                  be->be_suffix[0].bv_val, 0, 0 );
+
+       /* Check existence of home. Any error means trouble */
+       rc = stat( wi->wi_dbenv_home, &st );
+       if( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_db_open) ": database \"%s\": "
+                          "cannot access database directory \"%s\" (%d).\n",
+                          be->be_suffix[0].bv_val, wi->wi_dbenv_home, errno );
+               return -1;
+       }
+
+       /* Open and create database */
+       rc = wiredtiger_open(wi->wi_dbenv_home, NULL,
+                                                wi->wi_dbenv_config, &conn);
+       if( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_db_open) ": database \"%s\": "
+                          "cannot open database \"%s\" (%d).\n",
+                          be->be_suffix[0].bv_val, wi->wi_dbenv_home, errno );
+               return -1;
+       }
+
+       rc = conn->open_session(conn, NULL, NULL, &session);
+       if( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_db_open) ": database \"%s\": "
+                          "cannot open session: \"%s\"\n",
+                          be->be_suffix[0].bv_val, wiredtiger_strerror(rc), 0);
+               return -1;
+       }
+
+       rc = session->create(session,
+                                                WT_TABLE_ID2ENTRY,
+                                                "key_format=Q,"
+                                                "value_format=Su,"
+                                                "columns=(id,dn,entry)");
+       if( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_db_open) ": database \"%s\": "
+                          "cannot create entry table: \"%s\"\n",
+                          be->be_suffix[0].bv_val, wiredtiger_strerror(rc), 0);
+               return -1;
+       }
+
+       rc = session->create(session,
+                                                WT_TABLE_DN2ID,
+                                                "key_format=S,"
+                                                "value_format=QQS,"
+                                                "columns=(ndn,id,pid,revdn)");
+       if( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_db_open) ": database \"%s\": "
+                          "cannot create entry table: \"%s\"\n",
+                          be->be_suffix[0].bv_val, wiredtiger_strerror(rc), 0);
+               return -1;
+       }
+
+       /* not using dn2id index for id2entry table */
+       rc = session->create(session, WT_INDEX_DN, "columns=(dn)");
+       if( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_db_open) ": database \"%s\": "
+                          "cannot create dn index: \"%s\"\n",
+                          be->be_suffix[0].bv_val, wiredtiger_strerror(rc), 0);
+               return -1;
+       }
+
+       rc = session->create(session, WT_INDEX_PID, "columns=(pid)");
+       if( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_db_open) ": database \"%s\": "
+                          "cannot create pid index: \"%s\"\n",
+                          be->be_suffix[0].bv_val, wiredtiger_strerror(rc), 0);
+               return -1;
+       }
+
+       rc = session->create(session, WT_INDEX_REVDN, "columns=(revdn)");
+       if( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_db_open) ": database \"%s\": "
+                          "cannot create revdn index: \"%s\"\n",
+                          be->be_suffix[0].bv_val, wiredtiger_strerror(rc), 0);
+               return -1;
+       }
+
+       rc = wt_last_id( be, session, &wi->wi_lastid);
+       if (rc) {
+               snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
+                                 "last_id() failed: %s(%d).",
+                                 be->be_suffix[0].bv_val, wiredtiger_strerror(rc), rc );
+        Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_db_open) ": %s\n",
+                          cr->msg, 0, 0 );
+               return rc;
+       }
+
+       session->close(session, NULL);
+       wi->wi_conn = conn;
+       wi->wi_flags |= WT_IS_OPEN;
+
+    return LDAP_SUCCESS;
+}
+
+static int
+wt_db_close( BackendDB *be, ConfigReply *cr )
+{
+       struct wt_info *wi = (struct wt_info *) be->be_private;
+       int rc;
+
+       rc = wi->wi_conn->close(wi->wi_conn, NULL);
+       if( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_db_close)
+                          ": cannot close database (%d).\n",
+                          errno, 0, 0);
+               return -1;
+       }
+
+       wi->wi_flags &= ~WT_IS_OPEN;
+
+    return LDAP_SUCCESS;
+}
+
+static int
+wt_db_destroy( Backend *be, ConfigReply *cr )
+{
+       struct wt_info *wi = (struct wt_info *) be->be_private;
+
+       if( wi->wi_dbenv_home ) {
+               ch_free( wi->wi_dbenv_home );
+               wi->wi_dbenv_home = NULL;
+       }
+       if( wi->wi_dbenv_config ) {
+               ch_free( wi->wi_dbenv_config );
+               wi->wi_dbenv_config = NULL;
+       }
+
+       wt_attr_index_destroy( wi );
+       ch_free( wi );
+       be->be_private = NULL;
+
+       return LDAP_SUCCESS;
+}
+
+int
+wt_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,
+               NULL
+       };
+
+       /* initialize the database system */
+       Debug( LDAP_DEBUG_TRACE,
+                  LDAP_XSTRING(wt_back_initialize)
+                  ": initialize WiredTiger backend\n",
+                  0, 0, 0 );
+
+       bi->bi_flags |=
+               SLAP_BFLAG_INCREMENT |
+               SLAP_BFLAG_SUBENTRIES |
+               SLAP_BFLAG_ALIASES |
+               SLAP_BFLAG_REFERRALS;
+
+       bi->bi_controls = controls;
+
+       { /* version check */
+               Debug( LDAP_DEBUG_TRACE,
+                          LDAP_XSTRING(wt_back_initialize) ": %s\n",
+                          wiredtiger_version(NULL, NULL, NULL), 0, 0 );
+       }
+
+       bi->bi_open = 0;
+       bi->bi_close = 0;
+       bi->bi_config = 0;
+       bi->bi_destroy = 0;
+
+       bi->bi_db_init = wt_db_init;
+       bi->bi_db_config = config_generic_wrapper;
+       bi->bi_db_open = wt_db_open;
+       bi->bi_db_close = wt_db_close;
+       bi->bi_db_destroy = wt_db_destroy;
+
+       bi->bi_op_add = wt_add;
+       bi->bi_op_bind = wt_bind;
+       bi->bi_op_unbind = 0;
+       bi->bi_op_search = wt_search;
+       bi->bi_op_compare = wt_compare;
+       bi->bi_op_modify = 0;
+       bi->bi_op_modrdn = 0;
+       bi->bi_op_delete = wt_delete;
+       bi->bi_op_abandon = 0;
+
+       bi->bi_extended = 0;
+
+       bi->bi_chk_referrals = 0;
+       bi->bi_operational = wt_operational;
+
+       bi->bi_entry_release_rw = wt_entry_release;
+       bi->bi_entry_get_rw = wt_entry_get;
+
+       bi->bi_tool_entry_open = wt_tool_entry_open;
+       bi->bi_tool_entry_close = wt_tool_entry_close;
+       bi->bi_tool_entry_first = backend_tool_entry_first;
+       bi->bi_tool_entry_first_x = wt_tool_entry_first_x;
+       bi->bi_tool_entry_next = wt_tool_entry_next;
+       bi->bi_tool_entry_get = wt_tool_entry_get;
+       bi->bi_tool_entry_put = wt_tool_entry_put;
+       bi->bi_tool_entry_reindex = wt_tool_entry_reindex;
+
+       bi->bi_connection_init = 0;
+       bi->bi_connection_destroy = 0;
+
+       return wt_back_init_cf( bi );
+}
+
+#if SLAPD_WT == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+SLAP_BACKEND_INIT_MODULE( wt )
+
+#endif /* SLAPD_WT == SLAPD_MOD_DYNAMIC */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/key.c b/servers/slapd/back-wt/key.c
new file mode 100644 (file)
index 0000000..197a23b
--- /dev/null
@@ -0,0 +1,152 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "config.h"
+#include "idl.h"
+
+/* read a key */
+int
+wt_key_read(
+       Backend *be,
+       WT_CURSOR *cursor,
+       struct berval *k,
+       ID *ids,
+       WT_CURSOR **saved_cursor,
+       int get_flag
+       )
+{
+       int rc;
+       WT_ITEM key;
+       int exact;
+       WT_ITEM key2;
+       ID id;
+
+       Debug( LDAP_DEBUG_TRACE, "=> key_read\n", 0, 0, 0 );
+
+       WT_IDL_ZERO(ids);
+
+       bv2ITEM(k, &key);
+       cursor->set_key(cursor, &key, 0);
+       rc = cursor->search_near(cursor, &exact);
+       if( rc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_key_read)
+                          ": search_near failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+
+       do {
+               rc = cursor->get_key(cursor, &key2, &id);
+               if( rc ){
+                       Debug( LDAP_DEBUG_ANY,
+                                  LDAP_XSTRING(wt_key_read)
+                                  ": get_key failed: %s (%d)\n",
+                                  wiredtiger_strerror(rc), rc, 0 );
+                       break;
+               }
+
+               if (key.size != key2.size || memcmp(key.data, key2.data, key.size)) {
+                       if(exact < 0){
+                               rc = cursor->next(cursor);
+                               if (rc) {
+                                       break;
+                               }else{
+                                       continue;
+                               }
+                       }
+                       break;
+               }
+               exact = 0;
+               wt_idl_append_one(ids, id);
+               rc = cursor->next(cursor);
+       } while(rc == 0);
+
+       if (rc == WT_NOTFOUND ) {
+               rc = LDAP_SUCCESS;
+       }
+
+done:
+       if( rc != LDAP_SUCCESS ) {
+               Debug( LDAP_DEBUG_TRACE, "<= wt_key_read: failed (%d)\n",
+                          rc, 0, 0 );
+       } else {
+               Debug( LDAP_DEBUG_TRACE, "<= wt_key_read %ld candidates\n",
+                          (long) WT_IDL_N(ids), 0, 0 );
+       }
+
+       return rc;
+}
+
+/* Add or remove stuff from index files */
+int
+wt_key_change(
+       Backend *be,
+       WT_CURSOR *cursor,
+       struct berval *k,
+       ID id,
+       int op
+)
+{
+       int     rc;
+       WT_ITEM item;
+
+       Debug( LDAP_DEBUG_TRACE, "=> key_change(%s,%lx)\n",
+                  op == SLAP_INDEX_ADD_OP ? "ADD":"DELETE", (long) id, 0 );
+
+       bv2ITEM(k, &item);
+       cursor->set_key(cursor, &item, id);
+       cursor->set_value(cursor, NULL);
+
+       if (op == SLAP_INDEX_ADD_OP) {
+               /* Add values */
+               rc = cursor->insert(cursor);
+               if ( rc == WT_DUPLICATE_KEY ) rc = 0;
+       } else {
+               /* Delete values */
+               rc = cursor->remove(cursor);
+               if ( rc == WT_NOTFOUND ) rc = 0;
+       }
+       if( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_key_change)
+                          ": error: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0);
+               return rc;
+       }
+
+       Debug( LDAP_DEBUG_TRACE, "<= key_change %d\n", rc, 0, 0 );
+
+       return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/nextid.c b/servers/slapd/back-wt/nextid.c
new file mode 100644 (file)
index 0000000..06008c9
--- /dev/null
@@ -0,0 +1,92 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "config.h"
+
+int wt_next_id(BackendDB *be, ID *out){
+    struct wt_info *wi = (struct wt_info *) be->be_private;
+       *out = __sync_add_and_fetch(&wi->wi_lastid, 1);
+       return 0;
+}
+
+int wt_last_id( BackendDB *be, WT_SESSION *session, ID *out )
+{
+    WT_CURSOR *cursor;
+    int rc;
+    uint64_t id;
+
+    rc = session->open_cursor(session, WT_TABLE_ID2ENTRY, NULL, NULL, &cursor);
+    if(rc){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_last_id)
+                          ": open_cursor failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               return rc;
+    }
+
+    rc = cursor->prev(cursor);
+       switch(rc) {
+       case 0:
+               rc = cursor->get_key(cursor, &id);
+               if ( rc ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                  LDAP_XSTRING(wt_last_id)
+                                  ": get_key failed: %s (%d)\n",
+                                  wiredtiger_strerror(rc), rc, 0 );
+                       return rc;
+               }
+               *out = id;
+               break;
+       case WT_NOTFOUND:
+        /* no entry */
+        *out = 0;
+               break;
+       default:
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_last_id)
+                          ": prev failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+    }
+
+    rc = cursor->close(cursor);
+    if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_last_id)
+                          ": close failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               return rc;
+    }
+
+    return 0;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/operational.c b/servers/slapd/back-wt/operational.c
new file mode 100644 (file)
index 0000000..e2485cc
--- /dev/null
@@ -0,0 +1,113 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "config.h"
+
+int
+wt_hasSubordinates(
+       Operation *op,
+       Entry *e,
+       int *hasSubordinates )
+{
+       struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       wt_ctx *wc = NULL;
+       int rc;
+
+       assert( e != NULL );
+
+       wc = wt_ctx_get(op, wi);
+       if( !wc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_compare)
+                          ": wt_ctx_get failed\n",
+                          0, 0, 0 );
+               return LDAP_OTHER;
+       }
+
+       rc = wt_dn2id_has_children(op, wc->session, e->e_id);
+       switch(rc){
+       case 0:
+               *hasSubordinates = LDAP_COMPARE_TRUE;
+               break;
+       case WT_NOTFOUND:
+               *hasSubordinates = LDAP_COMPARE_FALSE;
+               rc = LDAP_SUCCESS;
+               break;
+       default:
+               Debug(LDAP_DEBUG_ANY,
+                         "<=- " LDAP_XSTRING(wt_hasSubordinates)
+                         ": has_children failed: %s (%d)\n",
+                         wiredtiger_strerror(rc), rc, 0 );
+               rc = LDAP_OTHER;
+       }
+       return rc;
+}
+
+/*
+ * sets the supported operational attributes (if required)
+ */
+int
+wt_operational(
+       Operation *op,
+       SlapReply *rs )
+{
+       Attribute   **ap;
+
+       assert( rs->sr_entry != NULL );
+
+       for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) {
+               if ( (*ap)->a_desc == slap_schema.si_ad_hasSubordinates ) {
+                       break;
+               }
+       }
+
+       if ( *ap == NULL &&
+                attr_find( rs->sr_entry->e_attrs, slap_schema.si_ad_hasSubordinates ) == NULL &&
+                ( SLAP_OPATTRS( rs->sr_attr_flags ) ||
+                  ad_inlist( slap_schema.si_ad_hasSubordinates, rs->sr_attrs ) ) )
+       {
+               int hasSubordinates, rc;
+
+               rc = wt_hasSubordinates( 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;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/proto-wt.h b/servers/slapd/back-wt/proto-wt.h
new file mode 100644 (file)
index 0000000..2fbbeb7
--- /dev/null
@@ -0,0 +1,177 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#ifndef _PROTO_WT_H_
+#define _PROTO_WT_H_
+
+LDAP_BEGIN_DECL
+
+#define WT_UCTYPE  "WT"
+
+AttrInfo *wt_attr_mask( struct wt_info *wi, AttributeDescription *desc );
+void wt_attr_flush( struct wt_info *wi );
+
+/*
+ * id2entry.c
+ */
+int wt_id2entry_add(Operation *op, WT_SESSION *session, Entry *e );
+int wt_id2entry_update(Operation *op, WT_SESSION *session, Entry *e );
+int wt_id2entry_delete(Operation *op, WT_SESSION *session, Entry *e );
+
+BI_entry_release_rw wt_entry_release;
+BI_entry_get_rw wt_entry_get;
+
+int wt_entry_return(Entry *e);
+int wt_entry_release(Operation *op, Entry *e, int rw);
+
+/*
+ * idl.c
+ */
+
+unsigned wt_idl_search( ID *ids, ID id );
+
+ID wt_idl_first( ID *ids, ID *cursor );
+ID wt_idl_next( ID *ids, ID *cursor );
+
+
+/*
+ * index.c
+ */
+int wt_index_entry LDAP_P(( Operation *op, wt_ctx *wc, int r, Entry *e ));
+
+#define wt_index_entry_add(op,t,e) \
+       wt_index_entry((op),(t),SLAP_INDEX_ADD_OP,(e))
+#define wt_index_entry_del(op,t,e) \
+       wt_index_entry((op),(t),SLAP_INDEX_DELETE_OP,(e))
+
+/*
+ * key.c
+ */
+int
+wt_key_read( Backend *be,
+                        WT_CURSOR *cursor,
+                        struct berval *k,
+                        ID *ids,
+                        WT_CURSOR **saved_cursor,
+                        int get_flag);
+
+int
+wt_key_change( Backend *be,
+                          WT_CURSOR *cursor,
+                          struct berval *k,
+                          ID id,
+                          int op);
+
+/*
+ * nextid.c
+ */
+int wt_next_id(BackendDB *be, ID *out);
+int wt_last_id( BackendDB *be, WT_SESSION *session, ID *out );
+
+/*
+ * config.c
+ */
+int wt_back_init_cf( BackendInfo *bi );
+
+/*
+ * dn2id.c
+ */
+
+int
+wt_dn2id(
+       Operation *op,
+       WT_SESSION *session,
+    struct berval *ndn,
+    ID *id);
+
+int
+wt_dn2id_add(
+       Operation *op,
+       WT_SESSION *session,
+       ID pid,
+       Entry *e);
+
+int
+wt_dn2id_delete(
+       Operation *op,
+       WT_SESSION *session,
+       struct berval *ndn);
+
+/*
+ * dn2entry.c
+ */
+int wt_dn2entry( BackendDB *be,
+                                wt_ctx *wc,
+                                struct berval *ndn,
+                                Entry **ep );
+
+int wt_dn2pentry( BackendDB *be,
+                                 wt_ctx *wc,
+                                 struct berval *ndn,
+                                 Entry **ep );
+
+/*
+ * former ctx.c
+ */
+wt_ctx *wt_ctx_init(struct wt_info *wi);
+void wt_ctx_free(void *key, void *data);
+wt_ctx *wt_ctx_get(Operation *op, struct wt_info *wi);
+WT_CURSOR *wt_ctx_index_cursor(wt_ctx *wc, struct berval *name, int create);
+
+
+/*
+ * former external.h
+ */
+
+extern BI_init              wt_back_initialize;
+extern BI_db_config         wt_db_config;
+extern BI_op_add            wt_add;
+extern BI_op_bind           wt_bind;
+extern BI_op_compare        wt_compare;
+extern BI_op_delete         wt_delete;
+
+extern BI_op_search         wt_search;
+
+extern BI_operational       wt_operational;
+
+/* tools.c */
+extern BI_tool_entry_open    wt_tool_entry_open;
+extern BI_tool_entry_close   wt_tool_entry_close;
+extern BI_tool_entry_first_x wt_tool_entry_first_x;
+extern BI_tool_entry_next    wt_tool_entry_next;
+extern BI_tool_entry_get     wt_tool_entry_get;
+extern BI_tool_entry_put     wt_tool_entry_put;
+extern BI_tool_entry_reindex wt_tool_entry_reindex;
+extern BI_tool_dn2id_get     wt_tool_dn2id_get;
+extern BI_tool_entry_modify  wt_tool_entry_modify;
+
+LDAP_END_DECL
+
+#endif /* _PROTO_WT_H */
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
+
diff --git a/servers/slapd/back-wt/search.c b/servers/slapd/back-wt/search.c
new file mode 100644 (file)
index 0000000..1b8910a
--- /dev/null
@@ -0,0 +1,709 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-wt.h"
+#include "idl.h"
+
+static int search_aliases(
+       Operation *op,
+       SlapReply *rs,
+       Entry *e,
+       WT_SESSION *session,
+       ID *ids,
+       ID *scopes,
+       ID *stack )
+{
+       /* TODO: search_aliases does not implement yet. */
+       WT_IDL_ZERO( ids );
+       return 0;
+}
+
+static int base_candidate(
+       BackendDB *be,
+       Entry *e,
+       ID *ids )
+{
+       Debug(LDAP_DEBUG_ARGS,
+                 LDAP_XSTRING(base_candidate)
+                 ": base: \"%s\" (0x%08lx)\n",
+                 e->e_nname.bv_val, (long) e->e_id, 0);
+
+       ids[0] = 1;
+       ids[1] = e->e_id;
+       return 0;
+}
+
+/* Look for "objectClass Present" in this filter.
+ * Also count depth of filter tree while we're at it.
+ */
+static int oc_filter(
+       Filter *f,
+       int cur,
+       int *max )
+{
+       int rc = 0;
+
+       assert( f != NULL );
+
+       if( cur > *max ) *max = cur;
+
+       switch( f->f_choice ) {
+       case LDAP_FILTER_PRESENT:
+               if (f->f_desc == slap_schema.si_ad_objectClass) {
+                       rc = 1;
+               }
+               break;
+
+       case LDAP_FILTER_AND:
+       case LDAP_FILTER_OR:
+               cur++;
+               for ( f=f->f_and; f; f=f->f_next ) {
+                       (void) oc_filter(f, cur, max);
+               }
+               break;
+
+       default:
+               break;
+       }
+       return rc;
+}
+
+static void search_stack_free( void *key, void *data )
+{
+       ber_memfree_x(data, NULL);
+}
+
+static void *search_stack( Operation *op )
+{
+       struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       void *ret = NULL;
+
+       if ( op->o_threadctx ) {
+               ldap_pvt_thread_pool_getkey( op->o_threadctx, (void *)search_stack,
+                                                                        &ret, NULL );
+       } else {
+               ret = wi->wi_search_stack;
+       }
+
+       if ( !ret ) {
+               ret = ch_malloc( wi->wi_search_stack_depth * WT_IDL_UM_SIZE
+                                                * sizeof( ID ) );
+               if ( op->o_threadctx ) {
+                       ldap_pvt_thread_pool_setkey( op->o_threadctx, (void *)search_stack,
+                                                                                ret, search_stack_free, NULL, NULL );
+               } else {
+                       wi->wi_search_stack = ret;
+               }
+       }
+       return ret;
+}
+
+static int search_candidates(
+       Operation *op,
+       SlapReply *rs,
+       Entry *e,
+       wt_ctx *wc,
+       ID  *ids,
+       ID  *scopes )
+{
+    struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       int rc, depth = 1;
+       Filter f, rf, xf, nf;
+       ID *stack;
+       AttributeAssertion aa_ref = ATTRIBUTEASSERTION_INIT;
+       Filter sf;
+       AttributeAssertion aa_subentry = ATTRIBUTEASSERTION_INIT;
+
+       Debug(LDAP_DEBUG_TRACE,
+                 LDAP_XSTRING(wt_search_candidates)
+                 ": base=\"%s\" (0x%08lx) scope=%d\n",
+                 e->e_nname.bv_val, (long) e->e_id, op->oq_search.rs_scope );
+
+       xf.f_or = op->oq_search.rs_filter;
+       xf.f_choice = LDAP_FILTER_OR;
+       xf.f_next = NULL;
+
+       /* If the user's filter uses objectClass=*,
+     * these clauses are redundant.
+     */
+       if (!oc_filter(op->oq_search.rs_filter, 1, &depth)
+               && !get_subentries_visibility(op)) {
+               if( !get_manageDSAit(op) && !get_domainScope(op) ) {
+                       /* match referral objects */
+                       struct berval bv_ref = BER_BVC( "referral" );
+                       rf.f_choice = LDAP_FILTER_EQUALITY;
+                       rf.f_ava = &aa_ref;
+                       rf.f_av_desc = slap_schema.si_ad_objectClass;
+                       rf.f_av_value = bv_ref;
+                       rf.f_next = xf.f_or;
+                       xf.f_or = &rf;
+                       depth++;
+               }
+       }
+
+       f.f_next = NULL;
+       f.f_choice = LDAP_FILTER_AND;
+       f.f_and = &nf;
+       /* Dummy; we compute scope separately now */
+       nf.f_choice = SLAPD_FILTER_COMPUTED;
+       nf.f_result = LDAP_SUCCESS;
+       nf.f_next = ( xf.f_or == op->oq_search.rs_filter )
+               ? op->oq_search.rs_filter : &xf ;
+       /* Filter depth increased again, adding dummy clause */
+       depth++;
+
+       if( get_subentries_visibility( op ) ) {
+               struct berval bv_subentry = BER_BVC( "subentry" );
+               sf.f_choice = LDAP_FILTER_EQUALITY;
+               sf.f_ava = &aa_subentry;
+               sf.f_av_desc = slap_schema.si_ad_objectClass;
+               sf.f_av_value = bv_subentry;
+               sf.f_next = nf.f_next;
+               nf.f_next = &sf;
+       }
+
+       /* Allocate IDL stack, plus 1 more for former tmp */
+       if ( depth+1 > wi->wi_search_stack_depth ) {
+               stack = ch_malloc( (depth + 1) * WT_IDL_UM_SIZE * sizeof( ID ) );
+       } else {
+               stack = search_stack( op );
+       }
+
+    if( op->ors_deref & LDAP_DEREF_SEARCHING ) {
+               rc = search_aliases( op, rs, e, wc->session, ids, scopes, stack );
+               if ( WT_IDL_IS_ZERO( ids ) && rc == LDAP_SUCCESS )
+                       rc = wt_dn2idl( op, wc->session, &e->e_nname, e, ids, stack );
+       } else {
+               rc = wt_dn2idl(op, wc->session, &e->e_nname, e, ids, stack );
+       }
+
+       if ( rc == LDAP_SUCCESS ) {
+               rc = wt_filter_candidates( op, wc, &f, ids,
+                                                                  stack, stack+WT_IDL_UM_SIZE );
+       }
+
+       if ( depth+1 > wi->wi_search_stack_depth ) {
+               ch_free( stack );
+       }
+
+    if( rc ) {
+               Debug(LDAP_DEBUG_TRACE,
+                         LDAP_XSTRING(wt_search_candidates)
+                         ": failed (rc=%d)\n",
+                         rc, NULL, NULL );
+
+       } else {
+               Debug(LDAP_DEBUG_TRACE,
+                         LDAP_XSTRING(wt_search_candidates)
+                         ": id=%ld first=%ld last=%ld\n",
+                         (long) ids[0],
+                         (long) WT_IDL_FIRST(ids),
+                         (long) WT_IDL_LAST(ids));
+       }
+       return 0;
+}
+
+static int
+parse_paged_cookie( Operation *op, SlapReply *rs )
+{
+       int     rc = LDAP_SUCCESS;
+       PagedResultsState *ps = op->o_pagedresults_state;
+
+       /* this function must be invoked only if the pagedResults
+     * control has been detected, parsed and partially checked
+     * by the frontend */
+       assert( get_pagedresults( op ) > SLAP_CONTROL_IGNORED );
+
+       /* cookie decoding/checks deferred to backend... */
+       if ( ps->ps_cookieval.bv_len ) {
+               PagedResultsCookie reqcookie;
+               if( ps->ps_cookieval.bv_len != sizeof( reqcookie ) ) {
+                       /* bad cookie */
+                       rs->sr_text = "paged results cookie is invalid";
+                       rc = LDAP_PROTOCOL_ERROR;
+                       goto done;
+               }
+
+               AC_MEMCPY( &reqcookie, ps->ps_cookieval.bv_val, sizeof( reqcookie ));
+
+               if ( reqcookie > ps->ps_cookie ) {
+                       /* bad cookie */
+                       rs->sr_text = "paged results cookie is invalid";
+                       rc = LDAP_PROTOCOL_ERROR;
+                       goto done;
+
+               } else if ( reqcookie < ps->ps_cookie ) {
+                       rs->sr_text = "paged results cookie is invalid or old";
+                       rc = LDAP_UNWILLING_TO_PERFORM;
+                       goto done;
+               }
+
+       } else {
+               /* we're going to use ps_cookie */
+               op->o_conn->c_pagedresults_state.ps_cookie = 0;
+       }
+
+done:;
+
+       return rc;
+}
+
+static void
+send_paged_response(
+       Operation   *op,
+       SlapReply   *rs,
+       ID      *lastid,
+       int     tentries )
+{
+       LDAPControl *ctrls[2];
+       BerElementBuffer berbuf;
+       BerElement  *ber = (BerElement *)&berbuf;
+       PagedResultsCookie respcookie;
+       struct berval cookie;
+
+       Debug(LDAP_DEBUG_ARGS,
+                 LDAP_XSTRING(send_paged_response)
+                 ": lastid=0x%08lx nentries=%d\n",
+                 lastid ? *lastid : 0, rs->sr_nentries, NULL );
+
+       ctrls[1] = NULL;
+
+       ber_init2( ber, NULL, LBER_USE_DER );
+
+       if ( lastid ) {
+               respcookie = ( PagedResultsCookie )(*lastid);
+               cookie.bv_len = sizeof( respcookie );
+               cookie.bv_val = (char *)&respcookie;
+
+       } else {
+               respcookie = ( PagedResultsCookie )0;
+               BER_BVSTR( &cookie, "" );
+       }
+
+       op->o_conn->c_pagedresults_state.ps_cookie = respcookie;
+       op->o_conn->c_pagedresults_state.ps_count =
+               ((PagedResultsState *)op->o_pagedresults_state)->ps_count +
+               rs->sr_nentries;
+
+       /* return size of 0 -- no estimate */
+       ber_printf( ber, "{iO}", 0, &cookie );
+
+       ctrls[0] = op->o_tmpalloc( sizeof(LDAPControl), op->o_tmpmemctx );
+       if ( ber_flatten2( ber, &ctrls[0]->ldctl_value, 0 ) == -1 ) {
+               goto done;
+       }
+
+       ctrls[0]->ldctl_oid = LDAP_CONTROL_PAGEDRESULTS;
+       ctrls[0]->ldctl_iscritical = 0;
+
+       slap_add_ctrls( op, rs, ctrls );
+       rs->sr_err = LDAP_SUCCESS;
+       send_ldap_result( op, rs );
+
+done:
+       (void) ber_free_buf( ber );
+}
+
+int
+wt_search( Operation *op, SlapReply *rs )
+{
+    struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       ID id, cursor;
+       ID lastid = NOID;
+       AttributeName *attrs;
+       OpExtra *oex;
+       int manageDSAit;
+       wt_ctx *wc;
+       int rc;
+       Entry *e = NULL;
+       Entry *base = NULL;
+       slap_mask_t mask;
+       time_t stoptime;
+
+       ID candidates[WT_IDL_UM_SIZE];
+       ID iscopes[WT_IDL_DB_SIZE];
+       ID scopes[WT_IDL_DB_SIZE];
+       int tentries = 0;
+       unsigned nentries = 0;
+
+       Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(wt_search) ": %s\n",
+                  op->o_req_dn.bv_val, 0, 0 );
+    attrs = op->oq_search.rs_attrs;
+
+       manageDSAit = get_manageDSAit( op );
+
+       wc = wt_ctx_get(op, wi);
+       if( !wc ){
+        Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_search)
+                          ": wt_ctx_get failed: %d\n",
+                          rc, 0, 0 );
+               send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+        return rc;
+       }
+
+       /* get entry */
+       rc = wt_dn2entry(op->o_bd, wc, &op->o_req_ndn, &e);
+       switch( rc ) {
+       case 0:
+               break;
+       case WT_NOTFOUND:
+               Debug( LDAP_DEBUG_ARGS,
+                          "<== " LDAP_XSTRING(wt_search)
+                          ": no such object %s\n",
+                          op->o_req_dn.bv_val, 0, 0);
+               rs->sr_err = LDAP_REFERRAL;
+               rs->sr_flags = REP_MATCHED_MUSTBEFREED | REP_REF_MUSTBEFREED;
+               send_ldap_result( op, rs );
+               goto done;
+       default:
+               /* TODO: error handling */
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_delete)
+                          ": error at wt_dn2entry() rc=%d\n",
+                          rc, 0, 0 );
+               send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+               goto done;
+       }
+
+       if ( op->ors_deref & LDAP_DEREF_FINDING ) {
+               /* not implement yet */
+       }
+
+       if ( e == NULL ) {
+               // TODO
+       }
+
+       /* NOTE: __NEW__ "search" access is required
+     * on searchBase object */
+       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;
+               }
+
+               send_ldap_result( op, rs );
+               goto done;
+       }
+
+       if ( !manageDSAit && is_entry_referral( e ) ) {
+               /* entry is a referral */
+               /* TODO: */
+       }
+
+       if ( get_assert( op ) &&
+                ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
+       {
+               rs->sr_err = LDAP_ASSERTION_FAILED;
+               send_ldap_result( op, rs );
+               goto done;
+       }
+
+       /* compute it anyway; root does not use it */
+       stoptime = op->o_time + op->ors_tlimit;
+
+       base = e;
+
+       e = NULL;
+
+       /* select candidates */
+       if ( op->oq_search.rs_scope == LDAP_SCOPE_BASE ) {
+               rs->sr_err = base_candidate( op->o_bd, base, candidates );
+       }else{
+               WT_IDL_ZERO( candidates );
+               WT_IDL_ZERO( scopes );
+               rc = search_candidates( op, rs, base,
+                                                               wc, candidates, scopes );
+               switch(rc){
+               case 0:
+               case WT_NOTFOUND:
+                       break;
+               default:
+                       Debug( LDAP_DEBUG_ANY,
+                                  LDAP_XSTRING(wt_search) ": error search_candidates\n",
+                                  0, 0, 0 );
+                       send_ldap_error( op, rs, LDAP_OTHER, "internal error" );
+                       goto done;
+               }
+       }
+
+       /* start cursor at beginning of candidates.
+     */
+       cursor = 0;
+
+       if ( candidates[0] == 0 ) {
+               Debug( LDAP_DEBUG_TRACE,
+                          LDAP_XSTRING(wt_search) ": no candidates\n",
+                          0, 0, 0 );
+               goto nochange;
+       }
+
+       if ( op->ors_limit &&
+                op->ors_limit->lms_s_unchecked != -1 &&
+                WT_IDL_N(candidates) > (unsigned) op->ors_limit->lms_s_unchecked )
+       {
+               rs->sr_err = LDAP_ADMINLIMIT_EXCEEDED;
+               send_ldap_result( op, rs );
+               rs->sr_err = LDAP_SUCCESS;
+               goto done;
+       }
+
+       if ( op->ors_limit == NULL  /* isroot == TRUE */ ||
+                !op->ors_limit->lms_s_pr_hide )
+       {
+               tentries = WT_IDL_N(candidates);
+       }
+
+       if ( get_pagedresults( op ) > SLAP_CONTROL_IGNORED ) {
+               /* TODO: pageresult */
+               PagedResultsState *ps = op->o_pagedresults_state;
+               /* deferred cookie parsing */
+               rs->sr_err = parse_paged_cookie( op, rs );
+               if ( rs->sr_err != LDAP_SUCCESS ) {
+                       send_ldap_result( op, rs );
+                       goto done;
+               }
+
+               cursor = (ID) ps->ps_cookie;
+               if ( cursor && ps->ps_size == 0 ) {
+                       rs->sr_err = LDAP_SUCCESS;
+                       rs->sr_text = "search abandoned by pagedResult size=0";
+                       send_ldap_result( op, rs );
+                       goto done;
+               }
+               id = wt_idl_first( candidates, &cursor );
+               if ( id == NOID ) {
+                       Debug( LDAP_DEBUG_TRACE,
+                                  LDAP_XSTRING(wt_search)
+                                  ": no paged results candidates\n",
+                                  0, 0, 0 );
+                       send_paged_response( op, rs, &lastid, 0 );
+
+                       rs->sr_err = LDAP_OTHER;
+                       goto done;
+               }
+               nentries = ps->ps_count;
+               if ( id == (ID)ps->ps_cookie )
+                       id = wt_idl_next( candidates, &cursor );
+               goto loop_begin;
+       }
+
+       for ( id = wt_idl_first( candidates, &cursor );
+                 id != NOID ; id = wt_idl_next( candidates, &cursor ) )
+       {
+               int scopeok;
+
+loop_begin:
+
+               /* check for abandon */
+               if ( op->o_abandon ) {
+                       rs->sr_err = SLAPD_ABANDON;
+                       send_ldap_result( op, rs );
+                       goto done;
+               }
+
+               /* mostly needed by internal searches,
+         * e.g. related to syncrepl, for whom
+         * abandon does not get set... */
+               if ( slapd_shutdown ) {
+                       rs->sr_err = LDAP_UNAVAILABLE;
+                       send_ldap_disconnect( op, rs );
+                       goto done;
+               }
+
+               /* check time limit */
+               if ( op->ors_tlimit != SLAP_NO_LIMIT
+                        && slap_get_time() > stoptime )
+               {
+                       rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+                       rs->sr_ref = rs->sr_v2ref;
+                       send_ldap_result( op, rs );
+                       rs->sr_err = LDAP_SUCCESS;
+                       goto done;
+               }
+
+               nentries++;
+
+       fetch_entry_retry:
+
+               rc = wt_id2entry(op->o_bd, wc->session, id, &e);
+               /* TODO: error handling */
+               if ( e == NULL ) {
+                       /* TODO: */
+                       goto loop_continue;
+               }
+               if ( is_entry_subentry( e ) ) {
+            if( op->oq_search.rs_scope != LDAP_SCOPE_BASE ) {
+                               if(!get_subentries_visibility( op )) {
+                                       /* only subentries are visible */
+                                       goto loop_continue;
+                               }
+
+                       } else if ( get_subentries( op ) &&
+                                               !get_subentries_visibility( op ))
+                       {
+                               /* only subentries are visible */
+                               goto loop_continue;
+                       }
+
+               } else if ( get_subentries_visibility( op )) {
+                       /* only subentries are visible */
+                       goto loop_continue;
+               }
+
+               scopeok = 0;
+               switch( op->ors_scope ) {
+               case LDAP_SCOPE_BASE:
+                       /* This is always true, yes? */
+                       if ( id == base->e_id ) scopeok = 1;
+                       break;
+               case LDAP_SCOPE_ONELEVEL:
+                       scopeok = 1;
+                       break;
+               case LDAP_SCOPE_SUBTREE:
+                       scopeok = 1;
+                       break;
+               }
+
+               /* aliases were already dereferenced in candidate list */
+               if ( op->ors_deref & LDAP_DEREF_SEARCHING ) {
+                       /* but if the search base is an alias, and we didn't
+                        * deref it when finding, return it.
+                        */
+                       if ( is_entry_alias(e) &&
+                                ((op->ors_deref & LDAP_DEREF_FINDING) ||
+                                 !bvmatch(&e->e_nname, &op->o_req_ndn)))
+                       {
+                               goto loop_continue;
+                       }
+                       /* TODO: alias handling */
+               }
+
+               /* Not in scope, ignore it */
+               if ( !scopeok )
+               {
+                       Debug( LDAP_DEBUG_TRACE,
+                                  LDAP_XSTRING(wt_search)
+                                  ": %ld scope not okay\n",
+                                  (long) id, 0, 0 );
+                       goto loop_continue;
+               }
+
+               /*
+         * if it's a referral, add it to the list of referrals. only do
+         * this for non-base searches, and don't check the filter
+         * explicitly here since it's only a candidate anyway.
+         */
+               if ( !manageDSAit && op->oq_search.rs_scope != LDAP_SCOPE_BASE
+                        && is_entry_referral( e ) )
+               {
+                       /* TODO: referral */
+               }
+
+               if ( !manageDSAit && is_entry_glue( e )) {
+                       goto loop_continue;
+               }
+
+               /* if it matches the filter and scope, send it */
+               rs->sr_err = test_filter( op, e, op->oq_search.rs_filter );
+               if ( rs->sr_err == LDAP_COMPARE_TRUE ) {
+                       /* check size limit */
+                       if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+                               /* TODO: */
+                       }
+
+                       if (e) {
+                               /* safe default */
+                               rs->sr_attrs = op->oq_search.rs_attrs;
+                               rs->sr_operational_attrs = NULL;
+                               rs->sr_ctrls = NULL;
+                               rs->sr_entry = e;
+                               RS_ASSERT( e->e_private != NULL );
+                               rs->sr_flags = REP_ENTRY_MUSTRELEASE;
+                               rs->sr_err = LDAP_SUCCESS;
+                               rs->sr_err = send_search_entry( op, rs );
+                               rs->sr_attrs = NULL;
+                               rs->sr_entry = NULL;
+                               e = NULL;
+                       }
+                       switch ( rs->sr_err ) {
+                       case LDAP_SUCCESS:  /* entry sent ok */
+                               break;
+                       default:
+                               /* TODO: error handling */
+                               break;
+                       }
+               } else {
+                       Debug( LDAP_DEBUG_TRACE,
+                                  LDAP_XSTRING(wt_search)
+                                  ": %ld does not match filter\n",
+                                  (long) id, 0, 0 );
+               }
+
+       loop_continue:
+               if( e ) {
+                       wt_entry_return( e );
+                       e = NULL;
+               }
+       }
+
+nochange:
+    rs->sr_ctrls = NULL;
+       rs->sr_ref = rs->sr_v2ref;
+       rs->sr_err = (rs->sr_v2ref == NULL) ? LDAP_SUCCESS : LDAP_REFERRAL;
+       rs->sr_rspoid = NULL;
+       if ( get_pagedresults(op) > SLAP_CONTROL_IGNORED ) {
+               /* not implement yet */
+               /* send_paged_response( op, rs, NULL, 0 ); */
+       } else {
+               send_ldap_result( op, rs );
+       }
+
+       rs->sr_err = LDAP_SUCCESS;
+
+done:
+
+       if( base ) {
+               wt_entry_return( base );
+       }
+
+       if( e ) {
+               wt_entry_return( e );
+       }
+
+    return rs->sr_err;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */
diff --git a/servers/slapd/back-wt/tools.c b/servers/slapd/back-wt/tools.c
new file mode 100644 (file)
index 0000000..950ea25
--- /dev/null
@@ -0,0 +1,514 @@
+/* OpenLDAP WiredTiger backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2002-2015 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 developed by HAMANO Tsukasa <hamano@osstech.co.jp>
+ * based on back-bdb for inclusion in OpenLDAP Software.
+ * WiredTiger is a product of MongoDB Inc.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include "back-wt.h"
+#include "config.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 int index_nattrs;
+
+static struct berval    *tool_base;
+static int      tool_scope;
+static Filter       *tool_filter;
+static Entry        *tool_next_entry;
+
+static wt_ctx *wc;
+static WT_CURSOR *reader;
+static WT_ITEM item;
+
+int
+wt_tool_entry_open( BackendDB *be, int mode )
+{
+    struct wt_info *wi = (struct wt_info *) be->be_private;
+       WT_CONNECTION *conn = wi->wi_conn;
+       int rc;
+
+       wc = wt_ctx_init(wi);
+    if( !wc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_tool_entry_open)
+                          ": wt_ctx_get failed: %s (%d)\n",
+                          0, 0, 0 );
+               return -1;
+    }
+
+       rc = wc->session->open_cursor(wc->session, WT_TABLE_ID2ENTRY"(entry)"
+                                                                 ,NULL, NULL, &reader);
+       if ( rc ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_tool_entry_open)
+                          ": cursor open failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               return -1;
+       }
+
+       return 0;
+}
+
+int
+wt_tool_entry_close( BackendDB *be )
+{
+       int rc;
+
+       if( reader ) {
+               reader->close(reader);
+               reader = NULL;
+       }
+
+       wt_ctx_free(NULL, wc);
+
+    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;
+}
+
+ID
+wt_tool_entry_first_x( BackendDB *be,
+                                          struct berval *base,
+                                          int scope,
+                                          Filter *f )
+{
+       tool_base = base;
+       tool_scope = scope;
+       tool_filter = f;
+
+       return wt_tool_entry_next( be );
+}
+
+ID
+wt_tool_entry_next( BackendDB *be )
+{
+       int rc;
+       ID id;
+
+       rc = reader->next(reader);
+       switch( rc ){
+       case 0:
+               break;
+       case WT_NOTFOUND:
+               return NOID;
+       default:
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_tool_entry_next)
+                          ": next failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               return NOID;
+       }
+
+       rc = reader->get_key(reader, &id);
+       if( rc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_tool_entry_next)
+                          ": get_key failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+       }
+
+       rc = reader->get_value(reader, &item);
+       if( rc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_tool_entry_next)
+                          ": get_value failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+       }
+       return id;
+}
+
+static ber_len_t
+entry_getlen(unsigned char **buf)
+{
+    ber_len_t len;
+    int i;
+
+    len = *(*buf)++;
+    if (len <= 0x7f)
+        return len;
+    i = len & 0x7f;
+    len = 0;
+    for (;i > 0; i--) {
+        len <<= 8;
+        len |= *(*buf)++;
+    }
+    return len;
+}
+
+int wt_entry_header(WT_ITEM *item, EntryHeader *eh){
+       unsigned char *ptr = (unsigned char *)item->data;
+
+    /* Some overlays can create empty entries
+     * so don't check for zeros here.
+     */
+       eh->nattrs = entry_getlen(&ptr);
+    eh->nvals = entry_getlen(&ptr);
+    eh->data = (char *)ptr;
+       return LDAP_SUCCESS;
+}
+
+Entry *
+wt_tool_entry_get( BackendDB *be, ID id )
+{
+       Entry *e = NULL;
+       static EntryHeader eh;
+       int rc, eoff;
+
+       assert( be != NULL );
+       assert( slapMode & SLAP_TOOL_MODE );
+
+       rc = wt_entry_header( &item,  &eh );
+       assert( rc == 0 );
+       eoff = eh.data - (char *)item.data;
+
+       eh.bv.bv_len = eh.nvals * sizeof( struct berval ) + item.size;
+       eh.bv.bv_val = ch_realloc( eh.bv.bv_val, eh.bv.bv_len );
+    memset(eh.bv.bv_val, 0xff, eh.bv.bv_len);
+       eh.data = eh.bv.bv_val + eh.nvals * sizeof( struct berval );
+    memcpy(eh.data, item.data, item.size);
+    eh.data += eoff;
+
+       rc = entry_decode( &eh, &e );
+       assert( rc == 0 );
+
+       if( rc == LDAP_SUCCESS ) {
+               e->e_id = id;
+       }
+
+       return e;
+}
+
+static int wt_tool_next_id(
+    Operation *op,
+    Entry *e,
+    struct berval *text,
+    int hole )
+{
+    struct wt_info *wi = (struct wt_info *) op->o_bd->be_private;
+       struct berval dn = e->e_name;
+       struct berval ndn = e->e_nname;
+       struct berval pdn, npdn;
+       int rc;
+       ID id = 0;
+       ID pid = 0;
+
+    if(ndn.bv_len == 0){
+        e->e_id = 0;
+        return 0;
+    }
+
+       rc = wt_dn2id(op, wc->session, &ndn, &id);
+       if(rc == 0){
+               e->e_id = id;
+       }else if( rc == WT_NOTFOUND ){
+               if ( !be_issuffix( op->o_bd, &ndn ) ) {
+                       ID eid = e->e_id;
+                       dnParent( &dn, &pdn );
+                       dnParent( &ndn, &npdn );
+                       e->e_name = pdn;
+                       e->e_nname = npdn;
+                       rc = wt_tool_next_id( op, e, text, 1 );
+                       e->e_name = dn;
+                       e->e_nname = ndn;
+                       if ( rc ) {
+                               return rc;
+                       }
+                       /* If parent didn't exist, it was created just now
+                        * and its ID is now in e->e_id. Make sure the current
+                        * entry gets added under the new parent ID.
+                        */
+                       if ( eid != e->e_id ) {
+                               pid = e->e_id;
+                       }
+               }else{
+                       pid = id;
+               }
+               wt_next_id( op->o_bd, &e->e_id );
+               rc = wt_dn2id_add(op, wc->session, pid, e);
+               if( rc ){
+                       snprintf( text->bv_val, text->bv_len,
+                                         "wt_dn2id_add failed: %s (%d)",
+                                         wiredtiger_strerror(rc), rc );
+                       Debug( LDAP_DEBUG_ANY,
+                                  "=> wt_tool_next_id: %s\n", text->bv_val, 0, 0 );
+               }
+
+       }else if ( !hole ) {
+               unsigned i, j;
+               e->e_id = id;
+
+               for ( i=0; i<nholes; i++) {
+                       if ( holes[i].id == e->e_id ) {
+                               free(holes[i].dn.bv_val);
+                               for (j=i;j<nholes;j++) holes[j] = holes[j+1];
+                               holes[j].id = 0;
+                               nholes--;
+                               break;
+                       } else if ( holes[i].id > e->e_id ) {
+                               break;
+                       }
+               }
+       }
+    return rc;
+}
+
+static int
+wt_tool_index_add(
+    Operation *op,
+    wt_ctx *wc,
+    Entry *e )
+{
+       return wt_index_entry_add( op, wc, e );
+}
+
+ID
+wt_tool_entry_put( BackendDB *be, Entry *e, struct berval *text )
+{
+    struct wt_info *wi = (struct wt_info *) be->be_private;
+    int rc;
+
+    Operation op = {0};
+    Opheader ohdr = {0};
+
+       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(wt_tool_entry_put)
+                  ": ( \"%s\" )\n", e->e_dn, 0, 0);
+
+    rc = wc->session->begin_transaction(wc->session, NULL);
+       if( rc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2id_add)
+                          ": begin_transaction failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               return NOID;
+       }
+
+       op.o_hdr = &ohdr;
+    op.o_bd = be;
+    op.o_tmpmemctx = NULL;
+    op.o_tmpmfuncs = &ch_mfuncs;
+
+    rc = wt_tool_next_id( &op, e, text, 0 );
+       if( rc != 0 ) {
+        snprintf( text->bv_val, text->bv_len,
+                                 "wt_tool_next_id failed: %s (%d)",
+                                 wiredtiger_strerror(rc), rc );
+        Debug( LDAP_DEBUG_ANY,
+                          "=> " LDAP_XSTRING(wt_tool_entry_put) ": %s\n",
+                          text->bv_val, 0, 0 );
+               goto done;
+       }
+
+       rc = wt_id2entry_add( &op, wc->session, e );
+       if( rc != 0 ) {
+        snprintf( text->bv_val, text->bv_len,
+                                 "id2entry_add failed: %s (%d)",
+                                 wiredtiger_strerror(rc), rc );
+        Debug( LDAP_DEBUG_ANY,
+                          "=> " LDAP_XSTRING(wt_tool_entry_put) ": %s\n",
+                          text->bv_val, 0, 0 );
+        goto done;
+    }
+
+       rc = wt_tool_index_add( &op, wc, e );
+    if( rc != 0 ) {
+        snprintf( text->bv_val, text->bv_len,
+                                 "index_entry_add failed: %s (%d)",
+                                 rc == LDAP_OTHER ? "Internal error" :
+                                 wiredtiger_strerror(rc), rc );
+        Debug( LDAP_DEBUG_ANY,
+                          "=> " LDAP_XSTRING(wt_tool_entry_put) ": %s\n",
+                          text->bv_val, 0, 0 );
+        goto done;
+    }
+
+done:
+       if ( rc == 0 ){
+               rc = wc->session->commit_transaction(wc->session, NULL);
+               if( rc != 0 ) {
+                       snprintf( text->bv_val, text->bv_len,
+                                         "txn_commit failed: %s (%d)",
+                                         wiredtiger_strerror(rc), rc );
+                       Debug( LDAP_DEBUG_ANY,
+                                  "=> " LDAP_XSTRING(wt_tool_entry_put) ": %s\n",
+                                  text->bv_val, 0, 0 );
+            e->e_id = NOID;
+               }
+       }else{
+               rc = wc->session->rollback_transaction(wc->session, NULL);
+               snprintf( text->bv_val, text->bv_len,
+                                 "txn_aborted! %s (%d)",
+                                 rc == LDAP_OTHER ? "Internal error" :
+                                 wiredtiger_strerror(rc), rc );
+        Debug( LDAP_DEBUG_ANY,
+                          "=> " LDAP_XSTRING(wt_tool_entry_put) ": %s\n",
+                          text->bv_val, 0, 0 );
+        e->e_id = NOID;
+       }
+
+       return e->e_id;
+}
+
+int wt_tool_entry_reindex(
+       BackendDB *be,
+       ID id,
+       AttributeDescription **adv )
+{
+       struct wt_info *wi = (struct wt_info *) be->be_private;
+       int rc;
+       Entry *e;
+       Operation op = {0};
+       Opheader ohdr = {0};
+
+       Debug( LDAP_DEBUG_ARGS,
+                  "=> " LDAP_XSTRING(wt_tool_entry_reindex) "( %ld )\n",
+                  (long) id, 0, 0 );
+       assert( tool_base == NULL );
+       assert( tool_filter == NULL );
+
+       /* No indexes configured, nothing to do. Could return an
+     * error here to shortcut things.
+     */
+       if (!wi->wi_attrs) {
+               return 0;
+       }
+
+       /* Check for explicit list of attrs to index */
+       if ( adv ) {
+               int i, j, n;
+
+               if ( wi->wi_attrs[0]->ai_desc != adv[0] ) {
+                       /* count */
+                       for ( n = 0; adv[n]; n++ ) ;
+
+                       /* insertion sort */
+                       for ( i = 0; i < n; i++ ) {
+                               AttributeDescription *ad = adv[i];
+                               for ( j = i-1; j>=0; j--) {
+                                       if ( SLAP_PTRCMP( adv[j], ad ) <= 0 ) break;
+                                       adv[j+1] = adv[j];
+                               }
+                               adv[j+1] = ad;
+                       }
+               }
+
+               for ( i = 0; adv[i]; i++ ) {
+                       if ( wi->wi_attrs[i]->ai_desc != adv[i] ) {
+                               for ( j = i+1; j < wi->wi_nattrs; j++ ) {
+                                       if ( wi->wi_attrs[j]->ai_desc == adv[i] ) {
+                                               AttrInfo *ai = wi->wi_attrs[i];
+                                               wi->wi_attrs[i] = wi->wi_attrs[j];
+                                               wi->wi_attrs[j] = ai;
+                                               break;
+                                       }
+                               }
+                               if ( j == wi->wi_nattrs ) {
+                                       Debug( LDAP_DEBUG_ANY,
+                                                  LDAP_XSTRING(wt_tool_entry_reindex)
+                                                  ": no index configured for %s\n",
+                                                  adv[i]->ad_cname.bv_val, 0, 0 );
+                                       return -1;
+                               }
+                       }
+               }
+               wi->wi_nattrs = i;
+       }
+
+       e = wt_tool_entry_get( be, id );
+
+       if( e == NULL ) {
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_tool_entry_reindex)
+                          ": could not locate id=%ld\n",
+                          (long) id, 0, 0 );
+               return -1;
+       }
+
+       op.o_hdr = &ohdr;
+       op.o_bd = be;
+       op.o_tmpmemctx = NULL;
+       op.o_tmpmfuncs = &ch_mfuncs;
+
+       rc = wc->session->begin_transaction(wc->session, NULL);
+       if( rc ){
+               Debug( LDAP_DEBUG_ANY,
+                          LDAP_XSTRING(wt_dn2id_add)
+                          ": begin_transaction failed: %s (%d)\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+               goto done;
+       }
+       Debug( LDAP_DEBUG_TRACE,
+                  "=> " LDAP_XSTRING(wt_tool_entry_reindex) "( %ld, \"%s\" )\n",
+                  (long) id, e->e_dn, 0 );
+
+       rc = wt_tool_index_add( &op, wc, e );
+
+done:
+       if ( rc == 0 ){
+               rc = wc->session->commit_transaction(wc->session, NULL);
+               if( rc ) {
+                       Debug( LDAP_DEBUG_ANY,
+                                  "=> " LDAP_XSTRING(wt_tool_entry_reindex)
+                                  "commit_transaction failed: %s (%d)\n",
+                                  wiredtiger_strerror(rc), rc, 0 );
+               }
+       }else{
+               rc = wc->session->rollback_transaction(wc->session, NULL);
+               Debug( LDAP_DEBUG_ANY,
+                          "=> " LDAP_XSTRING(wt_tool_entry_reindex)
+                          ": rollback transaction %s\n",
+                          wiredtiger_strerror(rc), rc, 0 );
+       }
+
+       wt_entry_release( &op, e, 0 );
+
+       return rc;
+}
+
+/*
+ * Local variables:
+ * indent-tabs-mode: t
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ */