From 61c95e7669d8f80af74deb3e844c7e056d30a64c Mon Sep 17 00:00:00 2001 From: HAMANO Tsukasa Date: Mon, 27 Apr 2015 10:27:58 +0900 Subject: [PATCH] ITS#8114 OpenLDAP WiredTiger Backend --- build/top.mk | 1 + configure.in | 54 +- doc/man/man5/slapd-wt.5 | 90 ++++ servers/slapd/back-wt/Makefile.in | 54 ++ servers/slapd/back-wt/add.c | 408 ++++++++++++++ servers/slapd/back-wt/attr.c | 388 ++++++++++++++ servers/slapd/back-wt/back-wt.h | 99 ++++ servers/slapd/back-wt/bind.c | 156 ++++++ servers/slapd/back-wt/compare.c | 149 ++++++ servers/slapd/back-wt/config.c | 158 ++++++ servers/slapd/back-wt/ctx.c | 142 +++++ servers/slapd/back-wt/delete.c | 424 +++++++++++++++ servers/slapd/back-wt/dn2entry.c | 131 +++++ servers/slapd/back-wt/dn2id.c | 393 ++++++++++++++ servers/slapd/back-wt/filterindex.c | 679 ++++++++++++++++++++++++ servers/slapd/back-wt/id2entry.c | 241 +++++++++ servers/slapd/back-wt/idl.c | 794 ++++++++++++++++++++++++++++ servers/slapd/back-wt/idl.h | 80 +++ servers/slapd/back-wt/index.c | 391 ++++++++++++++ servers/slapd/back-wt/init.c | 308 +++++++++++ servers/slapd/back-wt/key.c | 152 ++++++ servers/slapd/back-wt/nextid.c | 92 ++++ servers/slapd/back-wt/operational.c | 113 ++++ servers/slapd/back-wt/proto-wt.h | 177 +++++++ servers/slapd/back-wt/search.c | 709 +++++++++++++++++++++++++ servers/slapd/back-wt/tools.c | 514 ++++++++++++++++++ 26 files changed, 6895 insertions(+), 2 deletions(-) create mode 100644 doc/man/man5/slapd-wt.5 create mode 100644 servers/slapd/back-wt/Makefile.in create mode 100644 servers/slapd/back-wt/add.c create mode 100644 servers/slapd/back-wt/attr.c create mode 100644 servers/slapd/back-wt/back-wt.h create mode 100644 servers/slapd/back-wt/bind.c create mode 100644 servers/slapd/back-wt/compare.c create mode 100644 servers/slapd/back-wt/config.c create mode 100644 servers/slapd/back-wt/ctx.c create mode 100644 servers/slapd/back-wt/delete.c create mode 100644 servers/slapd/back-wt/dn2entry.c create mode 100644 servers/slapd/back-wt/dn2id.c create mode 100644 servers/slapd/back-wt/filterindex.c create mode 100644 servers/slapd/back-wt/id2entry.c create mode 100644 servers/slapd/back-wt/idl.c create mode 100644 servers/slapd/back-wt/idl.h create mode 100644 servers/slapd/back-wt/index.c create mode 100644 servers/slapd/back-wt/init.c create mode 100644 servers/slapd/back-wt/key.c create mode 100644 servers/slapd/back-wt/nextid.c create mode 100644 servers/slapd/back-wt/operational.c create mode 100644 servers/slapd/back-wt/proto-wt.h create mode 100644 servers/slapd/back-wt/search.c create mode 100644 servers/slapd/back-wt/tools.c diff --git a/build/top.mk b/build/top.mk index d6a751b0c5..3ac38ec0b4 100644 --- a/build/top.mk +++ b/build/top.mk @@ -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 diff --git a/configure.in b/configure.in index 72cd4c0631..cc95802472 100644 --- a/configure.in +++ b/configure.in @@ -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 index 0000000000..072e09b8b4 --- /dev/null +++ b/doc/man/man5/slapd-wt.5 @@ -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 \ +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\fR|\fBdefault\fR} [\fBpres\fR,\fBeq\fR,\fBapprox\fR,\fBsub\fR,\fI\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\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 . diff --git a/servers/slapd/back-wt/Makefile.in b/servers/slapd/back-wt/Makefile.in new file mode 100644 index 0000000000..e167645e51 --- /dev/null +++ b/servers/slapd/back-wt/Makefile.in @@ -0,0 +1,54 @@ +# Makefile.in for back-wt +# $OpenLDAP$ +## This work is part of OpenLDAP Software . +## +## 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 +## . + +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 index 0000000000..7dae9b13a7 --- /dev/null +++ b/servers/slapd/back-wt/add.c @@ -0,0 +1,408 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#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 index 0000000000..a681f21b5d --- /dev/null +++ b/servers/slapd/back-wt/attr.c @@ -0,0 +1,388 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * 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; iwi_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 index 0000000000..13742323f1 --- /dev/null +++ b/servers/slapd/back-wt/back-wt.h @@ -0,0 +1,99 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * 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 + +#include +#include + +#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 index 0000000000..e51243d082 --- /dev/null +++ b/servers/slapd/back-wt/bind.c @@ -0,0 +1,156 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#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 index 0000000000..dd34fbad79 --- /dev/null +++ b/servers/slapd/back-wt/compare.c @@ -0,0 +1,149 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#include + +#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 index 0000000000..6dca7f0994 --- /dev/null +++ b/servers/slapd/back-wt/config.c @@ -0,0 +1,158 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#include +#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 index 0000000000..3f4243a271 --- /dev/null +++ b/servers/slapd/back-wt/ctx.c @@ -0,0 +1,142 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * 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 index 0000000000..f90c83d9f4 --- /dev/null +++ b/servers/slapd/back-wt/delete.c @@ -0,0 +1,424 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#include + +#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 index 0000000000..26e716634c --- /dev/null +++ b/servers/slapd/back-wt/dn2entry.c @@ -0,0 +1,131 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#include +#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 index 0000000000..30337d0ac5 --- /dev/null +++ b/servers/slapd/back-wt/dn2id.c @@ -0,0 +1,393 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#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 index 0000000000..17aa7c2eb7 --- /dev/null +++ b/servers/slapd/back-wt/filterindex.c @@ -0,0 +1,679 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#include +#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 index 0000000000..e135d18f70 --- /dev/null +++ b/servers/slapd/back-wt/id2entry.c @@ -0,0 +1,241 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * 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 index 0000000000..6548e944aa --- /dev/null +++ b/servers/slapd/back-wt/idl.c @@ -0,0 +1,794 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#include + +#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 index 0000000000..52948a899a --- /dev/null +++ b/servers/slapd/back-wt/idl.h @@ -0,0 +1,80 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * 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<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 index 0000000000..812257a1db --- /dev/null +++ b/servers/slapd/back-wt/index.c @@ -0,0 +1,391 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#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 index 0000000000..1623532ced --- /dev/null +++ b/servers/slapd/back-wt/init.c @@ -0,0 +1,308 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#include +#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 index 0000000000..197a23bdff --- /dev/null +++ b/servers/slapd/back-wt/key.c @@ -0,0 +1,152 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#include +#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 index 0000000000..06008c9b18 --- /dev/null +++ b/servers/slapd/back-wt/nextid.c @@ -0,0 +1,92 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#include +#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 index 0000000000..e2485cc988 --- /dev/null +++ b/servers/slapd/back-wt/operational.c @@ -0,0 +1,113 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#include + +#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 index 0000000000..2fbbeb7bee --- /dev/null +++ b/servers/slapd/back-wt/proto-wt.h @@ -0,0 +1,177 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * 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 index 0000000000..1b8910a845 --- /dev/null +++ b/servers/slapd/back-wt/search.c @@ -0,0 +1,709 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#include + +#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 index 0000000000..950ea25332 --- /dev/null +++ b/servers/slapd/back-wt/tools.c @@ -0,0 +1,514 @@ +/* OpenLDAP WiredTiger backend */ +/* $OpenLDAP$ */ +/* This work is part of OpenLDAP Software . + * + * 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 + * . + */ +/* ACKNOWLEDGEMENTS: + * This work was developed by HAMANO Tsukasa + * based on back-bdb for inclusion in OpenLDAP Software. + * WiredTiger is a product of MongoDB Inc. + */ + +#include "portable.h" + +#include +#include +#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; inext(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; ie_id ) { + free(holes[i].dn.bv_val); + for (j=i;j 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: + */ -- 2.39.5