summaryrefslogtreecommitdiffstats
path: root/servers/slapd/back-ndb
diff options
context:
space:
mode:
Diffstat (limited to 'servers/slapd/back-ndb')
-rw-r--r--servers/slapd/back-ndb/Makefile.in59
-rw-r--r--servers/slapd/back-ndb/TODO6
-rw-r--r--servers/slapd/back-ndb/add.cpp347
-rw-r--r--servers/slapd/back-ndb/attrsets.conf36
-rw-r--r--servers/slapd/back-ndb/back-ndb.h168
-rw-r--r--servers/slapd/back-ndb/bind.cpp165
-rw-r--r--servers/slapd/back-ndb/compare.cpp169
-rw-r--r--servers/slapd/back-ndb/config.cpp333
-rw-r--r--servers/slapd/back-ndb/delete.cpp322
-rw-r--r--servers/slapd/back-ndb/init.cpp449
-rw-r--r--servers/slapd/back-ndb/modify.cpp704
-rw-r--r--servers/slapd/back-ndb/modrdn.cpp558
-rw-r--r--servers/slapd/back-ndb/ndbio.cpp1677
-rw-r--r--servers/slapd/back-ndb/proto-ndb.h166
-rw-r--r--servers/slapd/back-ndb/search.cpp854
-rw-r--r--servers/slapd/back-ndb/tools.cpp544
16 files changed, 6557 insertions, 0 deletions
diff --git a/servers/slapd/back-ndb/Makefile.in b/servers/slapd/back-ndb/Makefile.in
new file mode 100644
index 0000000..4df6898
--- /dev/null
+++ b/servers/slapd/back-ndb/Makefile.in
@@ -0,0 +1,59 @@
+# Makefile.in for back-ndb
+# $OpenLDAP$
+## This work is part of OpenLDAP Software <http://www.openldap.org/>.
+##
+## Copyright 2008-2022 The OpenLDAP Foundation.
+## All rights reserved.
+##
+## Redistribution and use in source and binary forms, with or without
+## modification, are permitted only as authorized by the OpenLDAP
+## Public License.
+##
+## A copy of this license is available in the file LICENSE in the
+## top-level directory of the distribution or, alternatively, at
+## <http://www.OpenLDAP.org/license.html>.
+##
+## ACKNOWLEDGEMENTS:
+## This work was initially developed by Howard Chu for inclusion
+## in OpenLDAP Software. This work was sponsored by MySQL.
+
+SRCS = init.cpp tools.cpp config.cpp ndbio.cpp \
+ add.cpp bind.cpp compare.cpp delete.cpp modify.cpp modrdn.cpp search.cpp
+
+OBJS = init.lo tools.lo config.lo ndbio.lo \
+ add.lo bind.lo compare.lo delete.lo modify.lo modrdn.lo search.lo
+
+LDAP_INCDIR= ../../../include
+LDAP_LIBDIR= ../../../libraries
+
+BUILD_OPT = "--enable-ndb"
+BUILD_MOD = @BUILD_NDB@
+
+mod_DEFS = -DSLAPD_IMPORT
+MOD_DEFS = $(@BUILD_NDB@_DEFS)
+MOD_LIBS = $(SLAPD_NDB_LIBS)
+
+shared_LDAP_LIBS = $(LDAP_LIBLDAP_LA) $(LDAP_LIBLBER_LA)
+NT_LINK_LIBS = -L.. -lslapd $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+UNIX_LINK_LIBS = $(@BUILD_LIBS_DYNAMIC@_LDAP_LIBS)
+
+LIBBASE = back_ndb
+
+XINCPATH = -I.. -I$(srcdir)/.. @SLAPD_NDB_INCS@
+XDEFS = $(MODULES_CPPFLAGS)
+
+AC_CXX = g++
+CXX = $(AC_CXX)
+LTCXX_MOD = $(LIBTOOL) $(LTONLY_MOD) --mode=compile \
+ $(CXX) $(LT_CFLAGS) $(LT_CPPFLAGS) $(MOD_DEFS) -c
+
+all-local-lib: ../.backend
+
+.SUFFIXES: .c .o .lo .cpp
+
+.cpp.lo:
+ $(LTCXX_MOD) $<
+
+../.backend: lib$(LIBBASE).a
+ @touch $@
+
diff --git a/servers/slapd/back-ndb/TODO b/servers/slapd/back-ndb/TODO
new file mode 100644
index 0000000..0393954
--- /dev/null
+++ b/servers/slapd/back-ndb/TODO
@@ -0,0 +1,6 @@
+LDAP features not currently supported:
+
+tagged attributes
+aliases
+substring indexing
+subtree rename
diff --git a/servers/slapd/back-ndb/add.cpp b/servers/slapd/back-ndb/add.cpp
new file mode 100644
index 0000000..f89cad4
--- /dev/null
+++ b/servers/slapd/back-ndb/add.cpp
@@ -0,0 +1,347 @@
+/* add.cpp - ldap NDB back-end add routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-ndb.h"
+
+extern "C" int
+ndb_back_add(Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ Entry p = {0};
+ Attribute poc;
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ NdbArgs NA;
+ NdbRdns rdns;
+ struct berval matched;
+ struct berval pdn, pndn;
+
+ int num_retries = 0;
+ int success;
+
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ Debug(LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(ndb_back_add) ": %s\n",
+ op->oq_add.rs_e->e_name.bv_val, 0, 0);
+
+ ctrls[num_ctrls] = 0;
+ NA.txn = NULL;
+
+ /* check entry's schema */
+ rs->sr_err = entry_schema_check( op, op->oq_add.rs_e, NULL,
+ get_relax(op), 1, NULL, &rs->sr_text, textbuf, textlen );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": entry failed schema check: "
+ "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
+ goto return_results;
+ }
+
+ /* add opattrs to shadow as well, only missing attrs will actually
+ * be added; helps compatibility with older OL versions */
+ rs->sr_err = slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
+ if ( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": entry failed op attrs add: "
+ "%s (%d)\n", rs->sr_text, rs->sr_err, 0 );
+ goto return_results;
+ }
+
+ /* Get our NDB handle */
+ rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+
+ /*
+ * Get the parent dn and see if the corresponding entry exists.
+ */
+ if ( be_issuffix( op->o_bd, &op->oq_add.rs_e->e_nname ) ) {
+ pdn = slap_empty_bv;
+ pndn = slap_empty_bv;
+ } else {
+ dnParent( &op->ora_e->e_name, &pdn );
+ dnParent( &op->ora_e->e_nname, &pndn );
+ }
+ p.e_name = op->ora_e->e_name;
+ p.e_nname = op->ora_e->e_nname;
+
+ op->ora_e->e_id = NOID;
+ rdns.nr_num = 0;
+ NA.rdns = &rdns;
+
+ if( 0 ) {
+retry: /* transaction retry */
+ NA.txn->close();
+ NA.txn = NULL;
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ goto return_results;
+ }
+ ndb_trans_backoff( ++num_retries );
+ }
+
+ NA.txn = NA.ndb->startTransaction();
+ rs->sr_text = NULL;
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* get entry or parent */
+ NA.e = &p;
+ NA.ocs = NULL;
+ rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
+ switch( rs->sr_err ) {
+ case 0:
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ goto return_results;
+ case LDAP_NO_SUCH_OBJECT:
+ break;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+#endif
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( NA.ocs ) {
+ int i;
+ for ( i=0; !BER_BVISNULL( &NA.ocs[i] ); i++ );
+ poc.a_numvals = i;
+ poc.a_desc = slap_schema.si_ad_objectClass;
+ poc.a_vals = NA.ocs;
+ poc.a_nvals = poc.a_vals;
+ poc.a_next = NULL;
+ p.e_attrs = &poc;
+ }
+
+ if ( ber_bvstrcasecmp( &pndn, &matched ) ) {
+ rs->sr_matched = matched.bv_val;
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": parent "
+ "does not exist\n", 0, 0, 0 );
+
+ rs->sr_text = "parent does not exist";
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ if ( p.e_attrs && is_entry_referral( &p )) {
+is_ref: p.e_attrs = NULL;
+ ndb_entry_get_data( op, &NA, 0 );
+ rs->sr_ref = get_entry_referrals( op, &p );
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags = REP_REF_MUSTBEFREED;
+ attrs_free( p.e_attrs );
+ p.e_attrs = NULL;
+ }
+ goto return_results;
+ }
+
+ p.e_name = pdn;
+ p.e_nname = pndn;
+ rs->sr_err = access_allowed( op, &p,
+ children, NULL, ACL_WADD, NULL );
+
+ if ( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": no write access to parent\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;
+ }
+
+ if ( NA.ocs ) {
+ if ( is_entry_subentry( &p )) {
+ /* parent is a subentry, don't allow add */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": parent is subentry\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
+ rs->sr_text = "parent is a subentry";
+ goto return_results;
+ }
+
+ if ( is_entry_alias( &p ) ) {
+ /* parent is an alias, don't allow add */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": parent is alias\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ rs->sr_text = "parent is an alias";
+ goto return_results;
+ }
+
+ if ( is_entry_referral( &p ) ) {
+ /* parent is a referral, don't allow add */
+ rs->sr_matched = p.e_name.bv_val;
+ goto is_ref;
+ }
+ }
+
+ rs->sr_err = access_allowed( op, op->ora_e,
+ entry, NULL, ACL_WADD, NULL );
+
+ if ( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": no write access to entry\n",
+ 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to entry";
+ goto return_results;;
+ }
+
+ /*
+ * Check ACL for attribute write access
+ */
+ if (!acl_check_modlist(op, op->ora_e, op->ora_modlist)) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(bdb_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;;
+ }
+
+
+ /* acquire entry ID */
+ if ( op->ora_e->e_id == NOID ) {
+ rs->sr_err = ndb_next_id( op->o_bd, NA.ndb, &op->ora_e->e_id );
+ if( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": next_id failed (%d)\n",
+ rs->sr_err, 0, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ if ( matched.bv_val )
+ rdns.nr_num++;
+ NA.e = op->ora_e;
+ /* dn2id index */
+ rs->sr_err = ndb_entry_put_info( op->o_bd, &NA, 0 );
+ if ( rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_info failed (%d)\n",
+ rs->sr_err, 0, 0 );
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* id2entry index */
+ rs->sr_err = ndb_entry_put_data( op->o_bd, &NA );
+ if ( rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": ndb_entry_put_data failed (%d) %s(%d)\n",
+ rs->sr_err, NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* post-read */
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if ( slap_read_controls( op, rs, op->oq_add.rs_e,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_add) ": post-read "
+ "failed!\n", 0, 0, 0 );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if ( op->o_noop ) {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn (no-op) failed";
+ } else {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ }
+
+ } else {
+ if(( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn_commit failed";
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ }
+
+ if ( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": %s : %s (%d)\n",
+ rs->sr_text, NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ NA.txn->close();
+ NA.txn = NULL;
+
+ Debug(LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_add) ": added%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ op->oq_add.rs_e->e_id, op->oq_add.rs_e->e_dn );
+
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ success = rs->sr_err;
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if( NA.txn != NULL ) {
+ NA.txn->execute( Rollback );
+ NA.txn->close();
+ }
+
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/attrsets.conf b/servers/slapd/back-ndb/attrsets.conf
new file mode 100644
index 0000000..f135e0a
--- /dev/null
+++ b/servers/slapd/back-ndb/attrsets.conf
@@ -0,0 +1,36 @@
+# Definition of useful attribute sets
+# from X.521 section 5
+#
+# TelecommunicationAttributeSet ATTRIBUTE ::= {
+# facsimileTelephoneNumber |
+# internationalISDNNumber |
+# telephoneNumber |
+# teletexTerminalIdentifier |
+# telexNumber |
+# preferredDeliveryMethod |
+# destinationIndicator |
+# registeredAddress |
+# x121Address }
+#
+# PostalAttributeSet ATTRIBUTE ::= {
+# physicalDeliveryOfficeName |
+# postalAddress |
+# postalCode |
+# postOfficeBox |
+# streetAddress }
+#
+# LocaleAttributeSet ATTRIBUTE ::= {
+# localityName |
+# stateOrProvinceName |
+# streetAddress }
+#
+# OrganizationalAttributeSet ATTRIBUTE ::= {
+# description |
+# LocaleAttributeSet |
+# PostalAttributeSet |
+# TelecommunicationAttributeSet |
+# businessCategory |
+# seeAlso |
+# searchGuide |
+# userPassword }
+
diff --git a/servers/slapd/back-ndb/back-ndb.h b/servers/slapd/back-ndb/back-ndb.h
new file mode 100644
index 0000000..c92c3f1
--- /dev/null
+++ b/servers/slapd/back-ndb/back-ndb.h
@@ -0,0 +1,168 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#ifndef SLAPD_NDB_H
+#define SLAPD_NDB_H
+
+#include "slap.h"
+
+#include <mysql.h>
+#include <NdbApi.hpp>
+
+LDAP_BEGIN_DECL
+
+/* The general design is to use one relational table per objectclass. This is
+ * complicated by objectclass inheritance and auxiliary classes though.
+ *
+ * Attributes must only occur in a single table. For objectclasses that inherit
+ * from other classes, attributes defined in the superior class are only stored
+ * in the superior class' table. When multiple unrelated classes define the same
+ * attributes, an attributeSet should be defined instead, containing all of the
+ * common attributes.
+ *
+ * The no_set table lists which other attributeSets apply to the current
+ * objectClass. The no_attrs table lists all of the non-inherited attributes of
+ * the class, including those residing in an attributeSet.
+ *
+ * Usually the table is named identically to the objectClass, but it can also
+ * be explicitly named something else if needed.
+ */
+#define NDB_MAX_OCSETS 8
+
+struct ndb_attrinfo;
+
+typedef struct ndb_ocinfo {
+ struct berval no_name; /* objectclass cname */
+ struct berval no_table;
+ ObjectClass *no_oc;
+ struct ndb_ocinfo *no_sets[NDB_MAX_OCSETS];
+ struct ndb_attrinfo **no_attrs;
+ int no_flag;
+ int no_nsets;
+ int no_nattrs;
+} NdbOcInfo;
+
+#define NDB_INFO_ATLEN 0x01
+#define NDB_INFO_ATSET 0x02
+#define NDB_INFO_INDEX 0x04
+#define NDB_INFO_ATBLOB 0x08
+
+typedef struct ndb_attrinfo {
+ struct berval na_name; /* attribute cname */
+ AttributeDescription *na_desc;
+ AttributeType *na_attr;
+ NdbOcInfo *na_oi;
+ int na_flag;
+ int na_len;
+ int na_column;
+ int na_ixcol;
+} NdbAttrInfo;
+
+typedef struct ListNode {
+ struct ListNode *ln_next;
+ void *ln_data;
+} ListNode;
+
+#define NDB_IS_OPEN(ni) (ni->ni_cluster != NULL)
+
+struct ndb_info {
+ /* NDB connection */
+ char *ni_connectstr;
+ char *ni_dbname;
+ Ndb_cluster_connection **ni_cluster;
+
+ /* MySQL connection parameters */
+ MYSQL ni_sql;
+ char *ni_hostname;
+ char *ni_username;
+ char *ni_password;
+ char *ni_socket;
+ unsigned long ni_clflag;
+ unsigned int ni_port;
+
+ /* Search filter processing */
+ int ni_search_stack_depth;
+ void *ni_search_stack;
+
+#define DEFAULT_SEARCH_STACK_DEPTH 16
+#define MINIMUM_SEARCH_STACK_DEPTH 8
+
+ /* Schema config */
+ NdbOcInfo *ni_opattrs;
+ ListNode *ni_attridxs;
+ ListNode *ni_attrlens;
+ ListNode *ni_attrsets;
+ ListNode *ni_attrblobs;
+ ldap_pvt_thread_rdwr_t ni_ai_rwlock;
+ Avlnode *ni_ai_tree;
+ ldap_pvt_thread_rdwr_t ni_oc_rwlock;
+ Avlnode *ni_oc_tree;
+ int ni_nconns; /* number of connections to open */
+ int ni_nextconn; /* next conn to use */
+ ldap_pvt_thread_mutex_t ni_conn_mutex;
+};
+
+#define NDB_MAX_RDNS 16
+#define NDB_RDN_LEN 128
+#define NDB_MAX_OCS 64
+
+#define DN2ID_TABLE "OL_dn2id"
+#define EID_COLUMN 0U
+#define VID_COLUMN 1U
+#define OCS_COLUMN 1U
+#define RDN_COLUMN 2U
+#define IDX_COLUMN (2U+NDB_MAX_RDNS)
+
+#define NEXTID_TABLE "OL_nextid"
+
+#define NDB_OC_BUFLEN 1026 /* 1024 data plus 2 len bytes */
+
+#define INDEX_NAME "OL_index"
+
+typedef struct NdbRdns {
+ short nr_num;
+ char nr_buf[NDB_MAX_RDNS][NDB_RDN_LEN+1];
+} NdbRdns;
+
+typedef struct NdbOcs {
+ int no_ninfo;
+ int no_ntext;
+ int no_nitext; /* number of implicit classes */
+ NdbOcInfo *no_info[NDB_MAX_OCS];
+ struct berval no_text[NDB_MAX_OCS];
+ struct berval no_itext[NDB_MAX_OCS]; /* implicit classes */
+} NdbOcs;
+
+typedef struct NdbArgs {
+ Ndb *ndb;
+ NdbTransaction *txn;
+ Entry *e;
+ NdbRdns *rdns;
+ struct berval *ocs;
+ int erdns;
+} NdbArgs;
+
+#define NDB_NO_SUCH_OBJECT 626
+#define NDB_ALREADY_EXISTS 630
+
+LDAP_END_DECL
+
+#include "proto-ndb.h"
+
+#endif
diff --git a/servers/slapd/back-ndb/bind.cpp b/servers/slapd/back-ndb/bind.cpp
new file mode 100644
index 0000000..f8d2df7
--- /dev/null
+++ b/servers/slapd/back-ndb/bind.cpp
@@ -0,0 +1,165 @@
+/* bind.cpp - ndb backend bind routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+
+#include "back-ndb.h"
+
+extern "C" int
+ndb_back_bind( Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ Entry e = {0};
+ Attribute *a;
+
+ AttributeDescription *password = slap_schema.si_ad_userPassword;
+
+ NdbArgs NA;
+
+ Debug( LDAP_DEBUG_ARGS,
+ "==> " LDAP_XSTRING(ndb_back_bind) ": dn: %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+
+ /* allow noauth binds */
+ switch ( be_rootdn_bind( op, NULL ) ) {
+ case LDAP_SUCCESS:
+ /* frontend will send result */
+ return rs->sr_err = LDAP_SUCCESS;
+
+ default:
+ /* give the database a chance */
+ break;
+ }
+
+ /* Get our NDB handle */
+ rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+ NA.e = &e;
+
+dn2entry_retry:
+ NA.txn = NA.ndb->startTransaction();
+ rs->sr_text = NULL;
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_bind) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto done;
+ }
+
+ /* get entry */
+ {
+ NdbRdns rdns;
+ rdns.nr_num = 0;
+ NA.rdns = &rdns;
+ NA.ocs = NULL;
+ rs->sr_err = ndb_entry_get_info( op, &NA, 0, NULL );
+ }
+ switch(rs->sr_err) {
+ case 0:
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap_server_busy";
+ goto done;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto dn2entry_retry;
+#endif
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto done;
+ }
+
+ rs->sr_err = ndb_entry_get_data( op, &NA, 0 );
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ ber_dupbv( &op->oq_bind.rb_edn, &e.e_name );
+
+ /* check for deleted */
+ if ( is_entry_subentry( &e ) ) {
+ /* entry is an subentry, don't allow bind */
+ Debug( LDAP_DEBUG_TRACE, "entry is subentry\n", 0,
+ 0, 0 );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( is_entry_alias( &e ) ) {
+ /* entry is an alias, don't allow bind */
+ Debug( LDAP_DEBUG_TRACE, "entry is alias\n", 0, 0, 0 );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( is_entry_referral( &e ) ) {
+ Debug( LDAP_DEBUG_TRACE, "entry is referral\n", 0,
+ 0, 0 );
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ switch ( op->oq_bind.rb_method ) {
+ case LDAP_AUTH_SIMPLE:
+ a = attr_find( e.e_attrs, password );
+ if ( a == NULL ) {
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ if ( slap_passwd_check( op, &e, a, &op->oq_bind.rb_cred,
+ &rs->sr_text ) != 0 )
+ {
+ /* failure; stop front end from sending result */
+ rs->sr_err = LDAP_INVALID_CREDENTIALS;
+ goto done;
+ }
+
+ rs->sr_err = 0;
+ break;
+
+ default:
+ assert( 0 ); /* should not be reachable */
+ rs->sr_err = LDAP_STRONG_AUTH_NOT_SUPPORTED;
+ rs->sr_text = "authentication method not supported";
+ }
+
+done:
+ NA.txn->close();
+ if ( e.e_attrs ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+ if ( rs->sr_err ) {
+ send_ldap_result( op, rs );
+ }
+ /* front end will send result on success (rs->sr_err==0) */
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/compare.cpp b/servers/slapd/back-ndb/compare.cpp
new file mode 100644
index 0000000..4fc7401
--- /dev/null
+++ b/servers/slapd/back-ndb/compare.cpp
@@ -0,0 +1,169 @@
+/* compare.cpp - ndb backend compare routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-ndb.h"
+
+int
+ndb_back_compare( Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ Entry e = {0};
+ Attribute *a;
+ int manageDSAit = get_manageDSAit( op );
+
+ NdbArgs NA;
+ NdbRdns rdns;
+ struct berval matched;
+
+ /* Get our NDB handle */
+ rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+
+ rdns.nr_num = 0;
+ NA.rdns = &rdns;
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+ NA.e = &e;
+
+dn2entry_retry:
+ NA.txn = NA.ndb->startTransaction();
+ rs->sr_text = NULL;
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_compare) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ NA.ocs = NULL;
+ /* get entry */
+ rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ rs->sr_matched = matched.bv_val;
+ if ( NA.ocs )
+ ndb_check_referral( op, rs, &NA );
+ goto return_results;
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto dn2entry_retry;
+#endif
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ rs->sr_err = ndb_entry_get_data( op, &NA, 0 );
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ if (!manageDSAit && is_entry_referral( &e ) ) {
+ /* return referral only if "disclose" is granted on the object */
+ if ( !access_allowed( op, &e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ /* entry is a referral, don't allow compare */
+ rs->sr_ref = get_entry_referrals( op, &e );
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = e.e_name.bv_val;
+ rs->sr_flags |= REP_REF_MUSTBEFREED;
+ }
+
+ Debug( LDAP_DEBUG_TRACE, "entry is referral\n", 0, 0, 0 );
+ goto return_results;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ if ( !access_allowed( op, &e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ }
+ goto return_results;
+ }
+
+ if ( !access_allowed( op, &e, op->oq_compare.rs_ava->aa_desc,
+ &op->oq_compare.rs_ava->aa_value, ACL_COMPARE, NULL ) )
+ {
+ /* return error only if "disclose"
+ * is granted on the object */
+ if ( !access_allowed( op, &e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL ) )
+ {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ } else {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ }
+ goto return_results;
+ }
+
+ rs->sr_err = LDAP_NO_SUCH_ATTRIBUTE;
+
+ for ( a = attrs_find( e.e_attrs, op->oq_compare.rs_ava->aa_desc );
+ a != NULL;
+ a = attrs_find( a->a_next, op->oq_compare.rs_ava->aa_desc ) )
+ {
+ rs->sr_err = LDAP_COMPARE_FALSE;
+
+ if ( attr_valfind( a,
+ SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH |
+ SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH,
+ &op->oq_compare.rs_ava->aa_value, NULL,
+ op->o_tmpmemctx ) == 0 )
+ {
+ rs->sr_err = LDAP_COMPARE_TRUE;
+ break;
+ }
+ }
+
+return_results:
+ NA.txn->close();
+ if ( e.e_attrs ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+ send_ldap_result( op, rs );
+
+ switch ( rs->sr_err ) {
+ case LDAP_COMPARE_FALSE:
+ case LDAP_COMPARE_TRUE:
+ rs->sr_err = LDAP_SUCCESS;
+ break;
+ }
+
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/config.cpp b/servers/slapd/back-ndb/config.cpp
new file mode 100644
index 0000000..8b79b8b
--- /dev/null
+++ b/servers/slapd/back-ndb/config.cpp
@@ -0,0 +1,333 @@
+/* config.cpp - ndb backend configuration file routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+#include "lutil.h"
+
+#include "back-ndb.h"
+
+#include "slap-config.h"
+
+extern "C" {
+ static ConfigDriver ndb_cf_gen;
+};
+
+enum {
+ NDB_ATLEN = 1,
+ NDB_ATSET,
+ NDB_INDEX,
+ NDB_ATBLOB
+};
+
+static ConfigTable ndbcfg[] = {
+ { "dbhost", "hostname", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_hostname),
+ "( OLcfgDbAt:6.1 NAME 'olcDbHost' "
+ "DESC 'Hostname of SQL server' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbname", "name", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_dbname),
+ "( OLcfgDbAt:6.2 NAME 'olcDbName' "
+ "DESC 'Name of SQL database' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbuser", "username", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_username),
+ "( OLcfgDbAt:6.3 NAME 'olcDbUser' "
+ "DESC 'Username for SQL session' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbpass", "password", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_password),
+ "( OLcfgDbAt:6.4 NAME 'olcDbPass' "
+ "DESC 'Password for SQL session' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbport", "port", 2, 2, 0, ARG_UINT|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_port),
+ "( OLcfgDbAt:6.5 NAME 'olcDbPort' "
+ "DESC 'Port number of SQL server' "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "dbsocket", "path", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_socket),
+ "( OLcfgDbAt:6.6 NAME 'olcDbSocket' "
+ "DESC 'Local socket path of SQL server' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbflag", "flag", 2, 2, 0, ARG_LONG|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_clflag),
+ "( OLcfgDbAt:6.7 NAME 'olcDbFlag' "
+ "DESC 'Flags for SQL session' "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "dbconnect", "hostname", 2, 2, 0, ARG_STRING|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_connectstr),
+ "( OLcfgDbAt:6.8 NAME 'olcDbConnect' "
+ "DESC 'Hostname of NDB server' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { "dbconnections", "number", 2, 2, 0, ARG_INT|ARG_OFFSET,
+ (void *)offsetof(struct ndb_info, ni_nconns),
+ "( OLcfgDbAt:6.9 NAME 'olcDbConnections' "
+ "DESC 'Number of cluster connections to open' "
+ "SYNTAX OMsInteger SINGLE-VALUE )", NULL, NULL },
+ { "attrlen", "attr> <len", 3, 3, 0, ARG_MAGIC|NDB_ATLEN,
+ (void *)ndb_cf_gen,
+ "( OLcfgDbAt:6.10 NAME 'olcNdbAttrLen' "
+ "DESC 'Column length of a specific attribute' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "attrset", "set> <attrs", 3, 3, 0, ARG_MAGIC|NDB_ATSET,
+ (void *)ndb_cf_gen,
+ "( OLcfgDbAt:6.11 NAME 'olcNdbAttrSet' "
+ "DESC 'Set of common attributes' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "index", "attr", 2, 2, 0, ARG_MAGIC|NDB_INDEX,
+ (void *)ndb_cf_gen, "( OLcfgDbAt:0.2 NAME 'olcDbIndex' "
+ "DESC 'Attribute to index' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "attrblob", "attr", 2, 2, 0, ARG_MAGIC|NDB_ATBLOB,
+ (void *)ndb_cf_gen, "( OLcfgDbAt:6.12 NAME 'olcNdbAttrBlob' "
+ "DESC 'Attribute to treat as a BLOB' "
+ "EQUALITY caseIgnoreMatch "
+ "SYNTAX OMsDirectoryString )", NULL, NULL },
+ { "directory", "dir", 2, 2, 0, ARG_IGNORED,
+ NULL, "( OLcfgDbAt:0.1 NAME 'olcDbDirectory' "
+ "DESC 'Dummy keyword' "
+ "SYNTAX OMsDirectoryString SINGLE-VALUE )", NULL, NULL },
+ { NULL, NULL, 0, 0, 0, ARG_IGNORED,
+ NULL, NULL, NULL, NULL }
+};
+
+static ConfigOCs ndbocs[] = {
+ {
+ "( OLcfgDbOc:6.2 "
+ "NAME 'olcNdbConfig' "
+ "DESC 'NDB backend configuration' "
+ "SUP olcDatabaseConfig "
+ "MUST ( olcDbHost $ olcDbName $ olcDbConnect ) "
+ "MAY ( olcDbUser $ olcDbPass $ olcDbPort $ olcDbSocket $ "
+ "olcDbFlag $ olcDbConnections $ olcNdbAttrLen $ "
+ "olcDbIndex $ olcNdbAttrSet $ olcNdbAttrBlob ) )",
+ Cft_Database, ndbcfg },
+ { NULL, Cft_Abstract, NULL }
+};
+
+static int
+ndb_cf_gen( ConfigArgs *c )
+{
+ struct ndb_info *ni = (struct ndb_info *)c->be->be_private;
+ int i, rc;
+ NdbAttrInfo *ai;
+ NdbOcInfo *oci;
+ ListNode *ln, **l2;
+ struct berval bv, *bva;
+
+ if ( c->op == SLAP_CONFIG_EMIT ) {
+ char buf[BUFSIZ];
+ rc = 0;
+ bv.bv_val = buf;
+ switch( c->type ) {
+ case NDB_ATLEN:
+ if ( ni->ni_attrlens ) {
+ for ( ln = ni->ni_attrlens; ln; ln=ln->ln_next ) {
+ ai = (NdbAttrInfo *)ln->ln_data;
+ bv.bv_len = snprintf( buf, sizeof(buf),
+ "%s %d", ai->na_name.bv_val,
+ ai->na_len );
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case NDB_ATSET:
+ if ( ni->ni_attrsets ) {
+ char *ptr, *end = buf+sizeof(buf);
+ for ( ln = ni->ni_attrsets; ln; ln=ln->ln_next ) {
+ oci = (NdbOcInfo *)ln->ln_data;
+ ptr = lutil_strcopy( buf, oci->no_name.bv_val );
+ *ptr++ = ' ';
+ for ( i=0; i<oci->no_nattrs; i++ ) {
+ if ( end - ptr < oci->no_attrs[i]->na_name.bv_len+1 )
+ break;
+ if ( i )
+ *ptr++ = ',';
+ ptr = lutil_strcopy(ptr,
+ oci->no_attrs[i]->na_name.bv_val );
+ }
+ bv.bv_len = ptr - buf;
+ value_add_one( &c->rvalue_vals, &bv );
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case NDB_INDEX:
+ if ( ni->ni_attridxs ) {
+ for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
+ ai = (NdbAttrInfo *)ln->ln_data;
+ value_add_one( &c->rvalue_vals, &ai->na_name );
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+
+ case NDB_ATBLOB:
+ if ( ni->ni_attrblobs ) {
+ for ( ln = ni->ni_attrblobs; ln; ln=ln->ln_next ) {
+ ai = (NdbAttrInfo *)ln->ln_data;
+ value_add_one( &c->rvalue_vals, &ai->na_name );
+ }
+ } else {
+ rc = 1;
+ }
+ break;
+
+ }
+ return rc;
+ } else if ( c->op == LDAP_MOD_DELETE ) { /* FIXME */
+ rc = 0;
+ switch( c->type ) {
+ case NDB_INDEX:
+ if ( c->valx == -1 ) {
+
+ /* delete all */
+
+ } else {
+
+ }
+ break;
+ }
+ return rc;
+ }
+
+ switch( c->type ) {
+ case NDB_ATLEN:
+ ber_str2bv( c->argv[1], 0, 0, &bv );
+ ai = ndb_ai_get( ni, &bv );
+ if ( !ai ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid attr %s",
+ c->log, c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ for ( ln = ni->ni_attrlens; ln; ln = ln->ln_next ) {
+ if ( ln->ln_data == (void *)ai ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: attr len already set for %s",
+ c->log, c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ }
+ ai->na_len = atoi( c->argv[2] );
+ ai->na_flag |= NDB_INFO_ATLEN;
+ ln = (ListNode *)ch_malloc( sizeof(ListNode));
+ ln->ln_data = ai;
+ ln->ln_next = NULL;
+ for ( l2 = &ni->ni_attrlens; *l2; l2 = &(*l2)->ln_next );
+ *l2 = ln;
+ break;
+
+ case NDB_INDEX:
+ ber_str2bv( c->argv[1], 0, 0, &bv );
+ ai = ndb_ai_get( ni, &bv );
+ if ( !ai ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid attr %s",
+ c->log, c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ for ( ln = ni->ni_attridxs; ln; ln = ln->ln_next ) {
+ if ( ln->ln_data == (void *)ai ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: attr index already set for %s",
+ c->log, c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ }
+ ai->na_flag |= NDB_INFO_INDEX;
+ ln = (ListNode *)ch_malloc( sizeof(ListNode));
+ ln->ln_data = ai;
+ ln->ln_next = NULL;
+ for ( l2 = &ni->ni_attridxs; *l2; l2 = &(*l2)->ln_next );
+ *l2 = ln;
+ break;
+
+ case NDB_ATSET:
+ ber_str2bv( c->argv[1], 0, 0, &bv );
+ bva = ndb_str2bvarray( c->argv[2], strlen( c->argv[2] ), ',', NULL );
+ rc = ndb_aset_get( ni, &bv, bva, &oci );
+ ber_bvarray_free( bva );
+ if ( rc ) {
+ if ( rc == LDAP_ALREADY_EXISTS ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s: attrset %s already defined",
+ c->log, c->argv[1] );
+ } else {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ),
+ "%s: invalid attrset %s (%d)",
+ c->log, c->argv[1], rc );
+ }
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ ln = (ListNode *)ch_malloc( sizeof(ListNode));
+ ln->ln_data = oci;
+ ln->ln_next = NULL;
+ for ( l2 = &ni->ni_attrsets; *l2; l2 = &(*l2)->ln_next );
+ *l2 = ln;
+ break;
+
+ case NDB_ATBLOB:
+ ber_str2bv( c->argv[1], 0, 0, &bv );
+ ai = ndb_ai_get( ni, &bv );
+ if ( !ai ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: invalid attr %s",
+ c->log, c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ for ( ln = ni->ni_attrblobs; ln; ln = ln->ln_next ) {
+ if ( ln->ln_data == (void *)ai ) {
+ snprintf( c->cr_msg, sizeof( c->cr_msg ), "%s: attr blob already set for %s",
+ c->log, c->argv[1] );
+ Debug( LDAP_DEBUG_ANY, "%s\n", c->cr_msg, 0, 0 );
+ return -1;
+ }
+ }
+ ai->na_flag |= NDB_INFO_ATBLOB;
+ ln = (ListNode *)ch_malloc( sizeof(ListNode));
+ ln->ln_data = ai;
+ ln->ln_next = NULL;
+ for ( l2 = &ni->ni_attrblobs; *l2; l2 = &(*l2)->ln_next );
+ *l2 = ln;
+ break;
+
+ }
+ return 0;
+}
+
+extern "C"
+int ndb_back_init_cf( BackendInfo *bi )
+{
+ bi->bi_cf_ocs = ndbocs;
+
+ return config_register_schema( ndbcfg, ndbocs );
+}
diff --git a/servers/slapd/back-ndb/delete.cpp b/servers/slapd/back-ndb/delete.cpp
new file mode 100644
index 0000000..46b33f6
--- /dev/null
+++ b/servers/slapd/back-ndb/delete.cpp
@@ -0,0 +1,322 @@
+/* delete.cpp - ndb backend delete routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "lutil.h"
+#include "back-ndb.h"
+
+static struct berval glue_bv = BER_BVC("glue");
+
+int
+ndb_back_delete( Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ Entry e = {0};
+ Entry p = {0};
+ int manageDSAit = get_manageDSAit( op );
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+
+ NdbArgs NA;
+ NdbRdns rdns;
+ struct berval matched;
+
+ int num_retries = 0;
+
+ int rc;
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ Debug( LDAP_DEBUG_ARGS, "==> " LDAP_XSTRING(ndb_back_delete) ": %s\n",
+ op->o_req_dn.bv_val, 0, 0 );
+
+ ctrls[num_ctrls] = 0;
+
+ /* allocate CSN */
+ if ( BER_BVISNULL( &op->o_csn ) ) {
+ struct berval csn;
+ char csnbuf[LDAP_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_dn, &p.e_name );
+ dnParent( &op->o_req_ndn, &p.e_nname );
+ }
+
+ /* Get our NDB handle */
+ rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+ rdns.nr_num = 0;
+ NA.rdns = &rdns;
+ NA.ocs = NULL;
+ NA.e = &e;
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+
+ if( 0 ) {
+retry: /* transaction retry */
+ NA.txn->close();
+ NA.txn = NULL;
+ Debug( LDAP_DEBUG_TRACE,
+ "==> " LDAP_XSTRING(ndb_back_delete) ": retrying...\n",
+ 0, 0, 0 );
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ goto return_results;
+ }
+ if ( NA.ocs ) {
+ ber_bvarray_free( NA.ocs );
+ NA.ocs = NULL;
+ }
+ ndb_trans_backoff( ++num_retries );
+ }
+
+ /* begin transaction */
+ NA.txn = NA.ndb->startTransaction();
+ rs->sr_text = NULL;
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_delete) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* get entry */
+ rs->sr_err = ndb_entry_get_info( op, &NA, 1, &matched );
+ switch( rs->sr_err ) {
+ case 0:
+ case LDAP_NO_SUCH_OBJECT:
+ break;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+#endif
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ||
+ ( !manageDSAit && bvmatch( NA.ocs, &glue_bv ))) {
+ Debug( LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(ndb_back_delete) ": no such object %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+ rs->sr_matched = matched.bv_val;
+ if ( NA.ocs )
+ ndb_check_referral( op, rs, &NA );
+ } else {
+ rs->sr_matched = p.e_name.bv_val;
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ }
+ goto return_results;
+ }
+
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, &p,
+ children, NULL, ACL_WDEL, NULL );
+
+ if ( !rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_delete) ": no write "
+ "access to parent\n", 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to parent";
+ goto return_results;
+ }
+
+ rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
+
+ rs->sr_err = access_allowed( op, &e,
+ entry, NULL, ACL_WDEL, NULL );
+
+ if ( !rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_delete) ": no write access "
+ "to entry\n", 0, 0, 0 );
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ rs->sr_text = "no write access to entry";
+ goto return_results;
+ }
+
+ if ( !manageDSAit && is_entry_referral( &e ) ) {
+ /* entry is a referral, don't allow delete */
+ rs->sr_ref = get_entry_referrals( op, &e );
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_delete) ": entry is referral\n",
+ 0, 0, 0 );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = e.e_name.bv_val;
+ rs->sr_flags = REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ /* pre-read */
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_delete) ": pre-read "
+ "failed!\n", 0, 0, 0 );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* Can't do it if we have kids */
+ rs->sr_err = ndb_has_children( &NA, &rc );
+ if ( rs->sr_err ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(ndb_back_delete)
+ ": has_children failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(ndb_back_delete)
+ ": non-leaf %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ rs->sr_text = "subordinate objects must be deleted first";
+ goto return_results;
+ }
+
+ /* delete info */
+ rs->sr_err = ndb_entry_del_info( op->o_bd, &NA );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_delete) ": del_info failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+ rs->sr_text = "DN index delete failed";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+
+ /* delete data */
+ rs->sr_err = ndb_entry_del_data( op->o_bd, &NA );
+ if ( rs->sr_err != 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_delete) ": del_data failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+ rs->sr_text = "entry delete failed";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+
+ if( op->o_noop ) {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn (no-op) failed";
+ } else {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ }
+ } else {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn_commit failed";
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ }
+
+ if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_delete) ": txn_%s failed: %s (%d)\n",
+ op->o_noop ? "abort (no-op)" : "commit",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "commit failed";
+
+ goto return_results;
+ }
+ NA.txn->close();
+ NA.txn = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_delete) ": deleted%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ e.e_id, op->o_req_dn.bv_val );
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if ( NA.ocs ) {
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ NA.ocs = NULL;
+ }
+
+ /* free entry */
+ if( e.e_attrs != NULL ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+
+ if( NA.txn != NULL ) {
+ NA.txn->execute( Rollback );
+ NA.txn->close();
+ }
+
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/init.cpp b/servers/slapd/back-ndb/init.cpp
new file mode 100644
index 0000000..d8f6276
--- /dev/null
+++ b/servers/slapd/back-ndb/init.cpp
@@ -0,0 +1,449 @@
+/* init.cpp - initialize ndb backend */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/unistd.h>
+#include <ac/stdlib.h>
+#include <ac/errno.h>
+#include <sys/stat.h>
+#include "back-ndb.h"
+#include <lutil.h>
+#include "slap-config.h"
+
+extern "C" {
+ static BI_db_init ndb_db_init;
+ static BI_db_close ndb_db_close;
+ static BI_db_open ndb_db_open;
+ static BI_db_destroy ndb_db_destroy;
+}
+
+static struct berval ndb_optable = BER_BVC("OL_opattrs");
+
+static struct berval ndb_opattrs[] = {
+ BER_BVC("structuralObjectClass"),
+ BER_BVC("entryUUID"),
+ BER_BVC("creatorsName"),
+ BER_BVC("createTimestamp"),
+ BER_BVC("entryCSN"),
+ BER_BVC("modifiersName"),
+ BER_BVC("modifyTimestamp"),
+ BER_BVNULL
+};
+
+static int ndb_oplens[] = {
+ 0, /* structuralOC, default */
+ 36, /* entryUUID */
+ 0, /* creatorsName, default */
+ 26, /* createTimestamp */
+ 40, /* entryCSN */
+ 0, /* modifiersName, default */
+ 26, /* modifyTimestamp */
+ -1
+};
+
+static Uint32 ndb_lastrow[1];
+NdbInterpretedCode *ndb_lastrow_code;
+
+static int
+ndb_db_init( BackendDB *be, ConfigReply *cr )
+{
+ struct ndb_info *ni;
+ int rc = 0;
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_db_init) ": Initializing ndb database\n",
+ 0, 0, 0 );
+
+ /* allocate backend-database-specific stuff */
+ ni = (struct ndb_info *) ch_calloc( 1, sizeof(struct ndb_info) );
+
+ be->be_private = ni;
+ be->be_cf_ocs = be->bd_info->bi_cf_ocs;
+
+ ni->ni_search_stack_depth = DEFAULT_SEARCH_STACK_DEPTH;
+
+ ldap_pvt_thread_rdwr_init( &ni->ni_ai_rwlock );
+ ldap_pvt_thread_rdwr_init( &ni->ni_oc_rwlock );
+ ldap_pvt_thread_mutex_init( &ni->ni_conn_mutex );
+
+#ifdef DO_MONITORING
+ rc = ndb_monitor_db_init( be );
+#endif
+
+ return rc;
+}
+
+static int
+ndb_db_close( BackendDB *be, ConfigReply *cr );
+
+static int
+ndb_db_open( BackendDB *be, ConfigReply *cr )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ char sqlbuf[BUFSIZ], *ptr;
+ int rc, i;
+
+ if ( be->be_suffix == NULL ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: need suffix" );
+ Debug( LDAP_DEBUG_ANY, "%s\n",
+ cr->msg, 0, 0 );
+ return -1;
+ }
+
+ Debug( LDAP_DEBUG_ARGS,
+ LDAP_XSTRING(ndb_db_open) ": \"%s\"\n",
+ be->be_suffix[0].bv_val, 0, 0 );
+
+ if ( ni->ni_nconns < 1 )
+ ni->ni_nconns = 1;
+
+ ni->ni_cluster = (Ndb_cluster_connection **)ch_calloc( ni->ni_nconns, sizeof( Ndb_cluster_connection *));
+ for ( i=0; i<ni->ni_nconns; i++ ) {
+ ni->ni_cluster[i] = new Ndb_cluster_connection( ni->ni_connectstr );
+ rc = ni->ni_cluster[i]->connect( 20, 5, 1 );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: ni_cluster[%d]->connect failed (%d)",
+ i, rc );
+ goto fail;
+ }
+ }
+ for ( i=0; i<ni->ni_nconns; i++ ) {
+ rc = ni->ni_cluster[i]->wait_until_ready( 30, 30 );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: ni_cluster[%d]->wait failed (%d)",
+ i, rc );
+ goto fail;
+ }
+ }
+
+ mysql_init( &ni->ni_sql );
+ if ( !mysql_real_connect( &ni->ni_sql, ni->ni_hostname, ni->ni_username, ni->ni_password,
+ "", ni->ni_port, ni->ni_socket, ni->ni_clflag )) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: mysql_real_connect failed, %s (%d)",
+ mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ rc = -1;
+ goto fail;
+ }
+
+ sprintf( sqlbuf, "CREATE DATABASE IF NOT EXISTS %s", ni->ni_dbname );
+ rc = mysql_query( &ni->ni_sql, sqlbuf );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: CREATE DATABASE %s failed, %s (%d)",
+ ni->ni_dbname, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ goto fail;
+ }
+
+ sprintf( sqlbuf, "USE %s", ni->ni_dbname );
+ rc = mysql_query( &ni->ni_sql, sqlbuf );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: USE DATABASE %s failed, %s (%d)",
+ ni->ni_dbname, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ goto fail;
+ }
+
+ ptr = sqlbuf;
+ ptr += sprintf( ptr, "CREATE TABLE IF NOT EXISTS " DN2ID_TABLE " ("
+ "eid bigint unsigned NOT NULL, "
+ "object_classes VARCHAR(1024) NOT NULL, "
+ "a0 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a1 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a2 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a3 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a4 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a5 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a6 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a7 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a8 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a9 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a10 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a11 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a12 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a13 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a14 VARCHAR(128) NOT NULL DEFAULT '', "
+ "a15 VARCHAR(128) NOT NULL DEFAULT '', "
+ "PRIMARY KEY (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14, a15), "
+ "UNIQUE KEY eid (eid) USING HASH" );
+ /* Create index columns */
+ if ( ni->ni_attridxs ) {
+ ListNode *ln;
+ int newcol = 0;
+
+ *ptr++ = ',';
+ *ptr++ = ' ';
+ for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
+ NdbAttrInfo *ai = (NdbAttrInfo *)ln->ln_data;
+ ptr += sprintf( ptr, "`%s` VARCHAR(%d), ",
+ ai->na_name.bv_val, ai->na_len );
+ }
+ ptr = lutil_strcopy(ptr, "KEY " INDEX_NAME " (" );
+
+ for ( ln = ni->ni_attridxs; ln; ln=ln->ln_next ) {
+ NdbAttrInfo *ai = (NdbAttrInfo *)ln->ln_data;
+ if ( newcol ) *ptr++ = ',';
+ *ptr++ = '`';
+ ptr = lutil_strcopy( ptr, ai->na_name.bv_val );
+ *ptr++ = '`';
+ ai->na_ixcol = newcol + 18;
+ newcol++;
+ }
+ *ptr++ = ')';
+ }
+ strcpy( ptr, ") ENGINE=ndb" );
+ rc = mysql_query( &ni->ni_sql, sqlbuf );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: CREATE TABLE " DN2ID_TABLE " failed, %s (%d)",
+ mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ goto fail;
+ }
+
+ rc = mysql_query( &ni->ni_sql, "CREATE TABLE IF NOT EXISTS " NEXTID_TABLE " ("
+ "a bigint unsigned AUTO_INCREMENT PRIMARY KEY ) ENGINE=ndb" );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: CREATE TABLE " NEXTID_TABLE " failed, %s (%d)",
+ mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ goto fail;
+ }
+
+ {
+ NdbOcInfo *oci;
+
+ rc = ndb_aset_get( ni, &ndb_optable, ndb_opattrs, &oci );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: ndb_aset_get( %s ) failed (%d)",
+ ndb_optable.bv_val, rc );
+ goto fail;
+ }
+ for ( i=0; ndb_oplens[i] >= 0; i++ ) {
+ if ( ndb_oplens[i] )
+ oci->no_attrs[i]->na_len = ndb_oplens[i];
+ }
+ rc = ndb_aset_create( ni, oci );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: ndb_aset_create( %s ) failed (%d)",
+ ndb_optable.bv_val, rc );
+ goto fail;
+ }
+ ni->ni_opattrs = oci;
+ }
+ /* Create attribute sets */
+ {
+ ListNode *ln;
+
+ for ( ln = ni->ni_attrsets; ln; ln=ln->ln_next ) {
+ NdbOcInfo *oci = (NdbOcInfo *)ln->ln_data;
+ rc = ndb_aset_create( ni, oci );
+ if ( rc ) {
+ snprintf( cr->msg, sizeof( cr->msg ),
+ "ndb_db_open: ndb_aset_create( %s ) failed (%d)",
+ oci->no_name.bv_val, rc );
+ goto fail;
+ }
+ }
+ }
+ /* Initialize any currently used objectClasses */
+ {
+ Ndb *ndb;
+ const NdbDictionary::Dictionary *myDict;
+
+ ndb = new Ndb( ni->ni_cluster[0], ni->ni_dbname );
+ ndb->init(1024);
+
+ myDict = ndb->getDictionary();
+ ndb_oc_read( ni, myDict );
+ delete ndb;
+ }
+
+#ifdef DO_MONITORING
+ /* monitor setup */
+ rc = ndb_monitor_db_open( be );
+ if ( rc != 0 ) {
+ goto fail;
+ }
+#endif
+
+ return 0;
+
+fail:
+ Debug( LDAP_DEBUG_ANY, "%s\n",
+ cr->msg, 0, 0 );
+ ndb_db_close( be, NULL );
+ return rc;
+}
+
+static int
+ndb_db_close( BackendDB *be, ConfigReply *cr )
+{
+ int i;
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+
+ mysql_close( &ni->ni_sql );
+ if ( ni->ni_cluster ) {
+ for ( i=0; i<ni->ni_nconns; i++ ) {
+ if ( ni->ni_cluster[i] ) {
+ delete ni->ni_cluster[i];
+ ni->ni_cluster[i] = NULL;
+ }
+ }
+ ch_free( ni->ni_cluster );
+ ni->ni_cluster = NULL;
+ }
+
+#ifdef DO_MONITORING
+ /* monitor handling */
+ (void)ndb_monitor_db_close( be );
+#endif
+
+ return 0;
+}
+
+static int
+ndb_db_destroy( BackendDB *be, ConfigReply *cr )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+
+#ifdef DO_MONITORING
+ /* monitor handling */
+ (void)ndb_monitor_db_destroy( be );
+#endif
+
+ ldap_pvt_thread_mutex_destroy( &ni->ni_conn_mutex );
+ ldap_pvt_thread_rdwr_destroy( &ni->ni_ai_rwlock );
+ ldap_pvt_thread_rdwr_destroy( &ni->ni_oc_rwlock );
+
+ ch_free( ni );
+ be->be_private = NULL;
+
+ return 0;
+}
+
+extern "C" int
+ndb_back_initialize(
+ BackendInfo *bi )
+{
+ static char *controls[] = {
+ LDAP_CONTROL_ASSERT,
+ LDAP_CONTROL_MANAGEDSAIT,
+ LDAP_CONTROL_NOOP,
+ LDAP_CONTROL_PAGEDRESULTS,
+ LDAP_CONTROL_PRE_READ,
+ LDAP_CONTROL_POST_READ,
+ LDAP_CONTROL_SUBENTRIES,
+ LDAP_CONTROL_X_PERMISSIVE_MODIFY,
+ LDAP_CONTROL_TXN_SPEC,
+ NULL
+ };
+
+ int rc = 0;
+
+ /* initialize the underlying database system */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_initialize) ": initialize ndb backend\n", 0, 0, 0 );
+
+ ndb_init();
+
+ ndb_lastrow_code = new NdbInterpretedCode( NULL, ndb_lastrow, 1 );
+ ndb_lastrow_code->interpret_exit_last_row();
+ ndb_lastrow_code->finalise();
+
+ bi->bi_flags |=
+ SLAP_BFLAG_INCREMENT |
+ SLAP_BFLAG_SUBENTRIES |
+ SLAP_BFLAG_ALIASES |
+ SLAP_BFLAG_REFERRALS;
+
+ bi->bi_controls = controls;
+
+ bi->bi_open = 0;
+ bi->bi_close = 0;
+ bi->bi_config = 0;
+ bi->bi_destroy = 0;
+
+ bi->bi_db_init = ndb_db_init;
+ bi->bi_db_config = config_generic_wrapper;
+ bi->bi_db_open = ndb_db_open;
+ bi->bi_db_close = ndb_db_close;
+ bi->bi_db_destroy = ndb_db_destroy;
+
+ bi->bi_op_add = ndb_back_add;
+ bi->bi_op_bind = ndb_back_bind;
+ bi->bi_op_compare = ndb_back_compare;
+ bi->bi_op_delete = ndb_back_delete;
+ bi->bi_op_modify = ndb_back_modify;
+ bi->bi_op_modrdn = ndb_back_modrdn;
+ bi->bi_op_search = ndb_back_search;
+
+ bi->bi_op_unbind = 0;
+
+#if 0
+ bi->bi_extended = ndb_extended;
+
+ bi->bi_chk_referrals = ndb_referrals;
+#endif
+ bi->bi_operational = ndb_operational;
+ bi->bi_has_subordinates = ndb_has_subordinates;
+ bi->bi_entry_release_rw = 0;
+ bi->bi_entry_get_rw = ndb_entry_get;
+
+ /*
+ * hooks for slap tools
+ */
+ bi->bi_tool_entry_open = ndb_tool_entry_open;
+ bi->bi_tool_entry_close = ndb_tool_entry_close;
+ bi->bi_tool_entry_first = ndb_tool_entry_first;
+ bi->bi_tool_entry_next = ndb_tool_entry_next;
+ bi->bi_tool_entry_get = ndb_tool_entry_get;
+ bi->bi_tool_entry_put = ndb_tool_entry_put;
+#if 0
+ bi->bi_tool_entry_reindex = ndb_tool_entry_reindex;
+ bi->bi_tool_sync = 0;
+ bi->bi_tool_dn2id_get = ndb_tool_dn2id_get;
+ bi->bi_tool_entry_modify = ndb_tool_entry_modify;
+#endif
+
+ bi->bi_connection_init = 0;
+ bi->bi_connection_destroy = 0;
+
+ rc = ndb_back_init_cf( bi );
+
+ return rc;
+}
+
+#if SLAPD_NDB == SLAPD_MOD_DYNAMIC
+
+/* conditionally define the init_module() function */
+extern "C" { int init_module( int argc, char *argv[] ); }
+
+SLAP_BACKEND_INIT_MODULE( ndb )
+
+#endif /* SLAPD_NDB == SLAPD_MOD_DYNAMIC */
+
diff --git a/servers/slapd/back-ndb/modify.cpp b/servers/slapd/back-ndb/modify.cpp
new file mode 100644
index 0000000..243e9d2
--- /dev/null
+++ b/servers/slapd/back-ndb/modify.cpp
@@ -0,0 +1,704 @@
+/* modify.cpp - ndb backend modify routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/time.h>
+
+#include "back-ndb.h"
+
+/* This is a copy from slapd/mods.c, but with compaction tweaked
+ * to swap values from the tail into deleted slots, to reduce the
+ * overall update traffic.
+ */
+static int
+ndb_modify_delete(
+ Entry *e,
+ Modification *mod,
+ int permissive,
+ const char **text,
+ char *textbuf, size_t textlen,
+ int *idx )
+{
+ Attribute *a;
+ MatchingRule *mr = mod->sm_desc->ad_type->sat_equality;
+ struct berval *cvals;
+ int *id2 = NULL;
+ int i, j, rc = 0, num;
+ unsigned flags;
+ char dummy = '\0';
+
+ /* For ordered vals, we have no choice but to preserve order */
+ if ( mod->sm_desc->ad_type->sat_flags & SLAP_AT_ORDERED_VAL )
+ return modify_delete_vindex( e, mod, permissive, text,
+ textbuf, textlen, idx );
+
+ /*
+ * If permissive is set, then the non-existence of an
+ * attribute is not treated as an error.
+ */
+
+ /* delete the entire attribute */
+ if ( mod->sm_values == NULL ) {
+ rc = attr_delete( &e->e_attrs, mod->sm_desc );
+
+ if( permissive ) {
+ rc = LDAP_SUCCESS;
+ } else if( rc != LDAP_SUCCESS ) {
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such attribute",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ return rc;
+ }
+
+ /* FIXME: Catch old code that doesn't set sm_numvals.
+ */
+ if ( !BER_BVISNULL( &mod->sm_values[mod->sm_numvals] )) {
+ for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ );
+ assert( mod->sm_numvals == i );
+ }
+ if ( !idx ) {
+ id2 = (int *)ch_malloc( mod->sm_numvals * sizeof( int ));
+ idx = id2;
+ }
+
+ if( mr == NULL || !mr->smr_match ) {
+ /* disallow specific attributes from being deleted if
+ no equality rule */
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no equality matching rule",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_INAPPROPRIATE_MATCHING;
+ goto return_result;
+ }
+
+ /* delete specific values - find the attribute first */
+ if ( (a = attr_find( e->e_attrs, mod->sm_desc )) == NULL ) {
+ if( permissive ) {
+ rc = LDAP_SUCCESS;
+ goto return_result;
+ }
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such attribute",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ goto return_result;
+ }
+
+ if ( mod->sm_nvalues ) {
+ flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX
+ | SLAP_MR_ASSERTED_VALUE_NORMALIZED_MATCH
+ | SLAP_MR_ATTRIBUTE_VALUE_NORMALIZED_MATCH;
+ cvals = mod->sm_nvalues;
+ } else {
+ flags = SLAP_MR_EQUALITY | SLAP_MR_VALUE_OF_ASSERTION_SYNTAX;
+ cvals = mod->sm_values;
+ }
+
+ /* Locate values to delete */
+ for ( i = 0; !BER_BVISNULL( &mod->sm_values[i] ); i++ ) {
+ unsigned sort;
+ rc = attr_valfind( a, flags, &cvals[i], &sort, NULL );
+ if ( rc == LDAP_SUCCESS ) {
+ idx[i] = sort;
+ } else if ( rc == LDAP_NO_SUCH_ATTRIBUTE ) {
+ if ( permissive ) {
+ idx[i] = -1;
+ continue;
+ }
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such value",
+ mod->sm_desc->ad_cname.bv_val );
+ goto return_result;
+ } else {
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: matching rule failed",
+ mod->sm_desc->ad_cname.bv_val );
+ goto return_result;
+ }
+ }
+
+ num = a->a_numvals;
+
+ /* Delete the values */
+ for ( i = 0; i < mod->sm_numvals; i++ ) {
+ /* Skip permissive values that weren't found */
+ if ( idx[i] < 0 )
+ continue;
+ /* Skip duplicate delete specs */
+ if ( a->a_vals[idx[i]].bv_val == &dummy )
+ continue;
+ /* delete value and mark it as gone */
+ free( a->a_vals[idx[i]].bv_val );
+ a->a_vals[idx[i]].bv_val = &dummy;
+ if( a->a_nvals != a->a_vals ) {
+ free( a->a_nvals[idx[i]].bv_val );
+ a->a_nvals[idx[i]].bv_val = &dummy;
+ }
+ a->a_numvals--;
+ }
+
+ /* compact array */
+ for ( i=0; i<num; i++ ) {
+ if ( a->a_vals[i].bv_val != &dummy )
+ continue;
+ for ( --num; num > i && a->a_vals[num].bv_val == &dummy; num-- )
+ ;
+ a->a_vals[i] = a->a_vals[num];
+ if ( a->a_nvals != a->a_vals )
+ a->a_nvals[i] = a->a_nvals[num];
+ }
+
+ BER_BVZERO( &a->a_vals[num] );
+ if (a->a_nvals != a->a_vals) {
+ BER_BVZERO( &a->a_nvals[num] );
+ }
+
+ /* if no values remain, delete the entire attribute */
+ if ( !a->a_numvals ) {
+ if ( attr_delete( &e->e_attrs, mod->sm_desc ) ) {
+ /* Can never happen */
+ *text = textbuf;
+ snprintf( textbuf, textlen,
+ "modify/delete: %s: no such attribute",
+ mod->sm_desc->ad_cname.bv_val );
+ rc = LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ }
+return_result:
+ if ( id2 )
+ ch_free( id2 );
+ return rc;
+}
+
+int ndb_modify_internal(
+ Operation *op,
+ NdbArgs *NA,
+ const char **text,
+ char *textbuf,
+ size_t textlen )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ Modification *mod;
+ Modifications *ml;
+ Modifications *modlist = op->orm_modlist;
+ NdbAttrInfo **modai, *atmp;
+ const NdbDictionary::Dictionary *myDict;
+ const NdbDictionary::Table *myTable;
+ int got_oc = 0, nmods = 0, nai = 0, i, j;
+ int rc, indexed = 0;
+ Attribute *old = NULL;
+
+ Debug( LDAP_DEBUG_TRACE, "ndb_modify_internal: 0x%08lx: %s\n",
+ NA->e->e_id, NA->e->e_dn, 0);
+
+ if ( !acl_check_modlist( op, NA->e, modlist )) {
+ return LDAP_INSUFFICIENT_ACCESS;
+ }
+
+ old = attrs_dup( NA->e->e_attrs );
+
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ mod = &ml->sml_mod;
+ nmods++;
+
+ switch ( mod->sm_op ) {
+ case LDAP_MOD_ADD:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: add %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ case LDAP_MOD_DELETE:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: delete %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ rc = ndb_modify_delete( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen, NULL );
+ assert( rc != LDAP_TYPE_OR_VALUE_EXISTS );
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ case LDAP_MOD_REPLACE:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: replace %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ rc = modify_replace_values( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ case LDAP_MOD_INCREMENT:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: increment %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ rc = modify_increment_values( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ case SLAP_MOD_SOFTADD:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: softadd %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ mod->sm_op = LDAP_MOD_ADD;
+
+ rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ mod->sm_op = SLAP_MOD_SOFTADD;
+
+ if ( rc == LDAP_TYPE_OR_VALUE_EXISTS ) {
+ rc = LDAP_SUCCESS;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ case SLAP_MOD_SOFTDEL:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: softdel %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ mod->sm_op = LDAP_MOD_DELETE;
+
+ rc = modify_delete_values( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ mod->sm_op = SLAP_MOD_SOFTDEL;
+
+ if ( rc == LDAP_NO_SUCH_ATTRIBUTE) {
+ rc = LDAP_SUCCESS;
+ }
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ case SLAP_MOD_ADD_IF_NOT_PRESENT:
+ Debug(LDAP_DEBUG_ARGS,
+ "ndb_modify_internal: add_if_not_present %s\n",
+ mod->sm_desc->ad_cname.bv_val, 0, 0);
+ if ( attr_find( NA->e->e_attrs, mod->sm_desc ) ) {
+ rc = LDAP_SUCCESS;
+ break;
+ }
+
+ mod->sm_op = LDAP_MOD_ADD;
+
+ rc = modify_add_values( NA->e, mod, get_permissiveModify(op),
+ text, textbuf, textlen );
+
+ mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
+
+ if( rc != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+ break;
+
+ default:
+ Debug(LDAP_DEBUG_ANY, "ndb_modify_internal: invalid op %d\n",
+ mod->sm_op, 0, 0);
+ *text = "Invalid modify operation";
+ rc = LDAP_OTHER;
+ Debug(LDAP_DEBUG_ARGS, "ndb_modify_internal: %d %s\n",
+ rc, *text, 0);
+ }
+
+ if ( rc != LDAP_SUCCESS ) {
+ attrs_free( old );
+ return rc;
+ }
+
+ /* If objectClass was modified, reset the flags */
+ if ( mod->sm_desc == slap_schema.si_ad_objectClass ) {
+ NA->e->e_ocflags = 0;
+ got_oc = 1;
+ }
+ }
+
+ /* check that the entry still obeys the schema */
+ rc = entry_schema_check( op, NA->e, NULL, get_relax(op), 0, NULL,
+ text, textbuf, textlen );
+ if ( rc != LDAP_SUCCESS || op->o_noop ) {
+ if ( rc != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_ANY,
+ "entry failed schema check: %s\n",
+ *text, 0, 0 );
+ }
+ attrs_free( old );
+ return rc;
+ }
+
+ if ( got_oc ) {
+ rc = ndb_entry_put_info( op->o_bd, NA, 1 );
+ if ( rc ) {
+ attrs_free( old );
+ return rc;
+ }
+ }
+
+ /* apply modifications to DB */
+ modai = (NdbAttrInfo **)op->o_tmpalloc( nmods * sizeof(NdbAttrInfo*), op->o_tmpmemctx );
+
+ /* Get the unique list of modified attributes */
+ ldap_pvt_thread_rdwr_rlock( &ni->ni_ai_rwlock );
+ for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
+ /* Already took care of objectclass */
+ if ( ml->sml_desc == slap_schema.si_ad_objectClass )
+ continue;
+ for ( i=0; i<nai; i++ ) {
+ if ( ml->sml_desc->ad_type == modai[i]->na_attr )
+ break;
+ }
+ /* This attr was already updated */
+ if ( i < nai )
+ continue;
+ modai[nai] = ndb_ai_find( ni, ml->sml_desc->ad_type );
+ if ( modai[nai]->na_flag & NDB_INFO_INDEX )
+ indexed++;
+ nai++;
+ }
+ ldap_pvt_thread_rdwr_runlock( &ni->ni_ai_rwlock );
+
+ /* If got_oc, this was already done above */
+ if ( indexed && !got_oc) {
+ rc = ndb_entry_put_info( op->o_bd, NA, 1 );
+ if ( rc ) {
+ attrs_free( old );
+ return rc;
+ }
+ }
+
+ myDict = NA->ndb->getDictionary();
+
+ /* sort modai so that OcInfo's are contiguous */
+ {
+ int j, k;
+ for ( i=0; i<nai; i++ ) {
+ for ( j=i+1; j<nai; j++ ) {
+ if ( modai[i]->na_oi == modai[j]->na_oi )
+ continue;
+ for ( k=j+1; k<nai; k++ ) {
+ if ( modai[i]->na_oi == modai[k]->na_oi ) {
+ atmp = modai[j];
+ modai[j] = modai[k];
+ modai[k] = atmp;
+ break;
+ }
+ }
+ /* there are no more na_oi's that match modai[i] */
+ if ( k == nai ) {
+ i = j;
+ }
+ }
+ }
+ }
+
+ /* One call per table... */
+ for ( i=0; i<nai; i += j ) {
+ atmp = modai[i];
+ for ( j=i+1; j<nai; j++ )
+ if ( atmp->na_oi != modai[j]->na_oi )
+ break;
+ j -= i;
+ myTable = myDict->getTable( atmp->na_oi->no_table.bv_val );
+ if ( !myTable )
+ continue;
+ rc = ndb_oc_attrs( NA->txn, myTable, NA->e, atmp->na_oi, &modai[i], j, old );
+ if ( rc ) break;
+ }
+ attrs_free( old );
+ return rc;
+}
+
+
+int
+ndb_back_modify( Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ Entry e = {0};
+ int manageDSAit = get_manageDSAit( op );
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+
+ int num_retries = 0;
+
+ NdbArgs NA;
+ NdbRdns rdns;
+ struct berval matched;
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ Debug( LDAP_DEBUG_ARGS, LDAP_XSTRING(ndb_back_modify) ": %s\n",
+ op->o_req_dn.bv_val, 0, 0 );
+
+ ctrls[num_ctrls] = NULL;
+
+ slap_mods_opattrs( op, &op->orm_modlist, 1 );
+
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+
+ /* Get our NDB handle */
+ rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+ rdns.nr_num = 0;
+ NA.rdns = &rdns;
+ NA.e = &e;
+
+ if( 0 ) {
+retry: /* transaction retry */
+ NA.txn->close();
+ NA.txn = NULL;
+ if( e.e_attrs ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+ Debug(LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modify) ": retrying...\n", 0, 0, 0);
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ goto return_results;
+ }
+ if ( NA.ocs ) {
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ }
+ ndb_trans_backoff( ++num_retries );
+ }
+ NA.ocs = NULL;
+
+ /* begin transaction */
+ NA.txn = NA.ndb->startTransaction();
+ rs->sr_text = NULL;
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modify) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* get entry or ancestor */
+ rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ Debug( LDAP_DEBUG_ARGS,
+ "<=- ndb_back_modify: no such object %s\n",
+ op->o_req_dn.bv_val, 0, 0 );
+ rs->sr_matched = matched.bv_val;
+ if (NA.ocs )
+ ndb_check_referral( op, rs, &NA );
+ goto return_results;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+#endif
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* acquire and lock entry */
+ rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
+
+ if ( !manageDSAit && is_entry_referral( &e ) ) {
+ /* entry is a referral, don't allow modify */
+ rs->sr_ref = get_entry_referrals( op, &e );
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modify) ": entry is referral\n",
+ 0, 0, 0 );
+
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_matched = e.e_name.bv_val;
+ rs->sr_flags = REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &e, (Filter*)get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if ( slap_read_controls( op, rs, &e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modify) ": pre-read "
+ "failed!\n", 0, 0, 0 );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* Modify the entry */
+ rs->sr_err = ndb_modify_internal( op, &NA, &rs->sr_text, textbuf, textlen );
+
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modify) ": modify failed (%d)\n",
+ rs->sr_err, 0, 0 );
+#if 0
+ switch( rs->sr_err ) {
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+ }
+#endif
+ goto return_results;
+ }
+
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &e,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modify)
+ ": post-read failed!\n", 0, 0, 0 );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if( op->o_noop ) {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn_abort (no-op) failed";
+ } else {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ }
+ } else {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn_commit failed";
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ }
+
+ if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modify) ": txn_%s failed: %s (%d)\n",
+ op->o_noop ? "abort (no-op)" : "commit",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ NA.txn->close();
+ NA.txn = NULL;
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modify) ": updated%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ e.e_id, op->o_req_dn.bv_val );
+
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if ( NA.ocs ) {
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ NA.ocs = NULL;
+ }
+
+ if ( e.e_attrs != NULL ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+
+ if( NA.txn != NULL ) {
+ NA.txn->execute( Rollback );
+ NA.txn->close();
+ }
+
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+
+ rs->sr_text = NULL;
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/modrdn.cpp b/servers/slapd/back-ndb/modrdn.cpp
new file mode 100644
index 0000000..ecc3c8e
--- /dev/null
+++ b/servers/slapd/back-ndb/modrdn.cpp
@@ -0,0 +1,558 @@
+/* modrdn.cpp - ndb backend modrdn routine */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+
+#include "back-ndb.h"
+
+int
+ndb_back_modrdn( Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ AttributeDescription *children = slap_schema.si_ad_children;
+ AttributeDescription *entry = slap_schema.si_ad_entry;
+ struct berval new_dn = BER_BVNULL, new_ndn = BER_BVNULL;
+ Entry e = {0};
+ Entry e2 = {0};
+ char textbuf[SLAP_TEXT_BUFLEN];
+ size_t textlen = sizeof textbuf;
+
+ struct berval *np_dn = NULL; /* newSuperior dn */
+ struct berval *np_ndn = NULL; /* newSuperior ndn */
+
+ int manageDSAit = get_manageDSAit( op );
+ int num_retries = 0;
+
+ NdbArgs NA, NA2;
+ NdbRdns rdns, rdn2;
+ struct berval matched;
+
+ LDAPControl **preread_ctrl = NULL;
+ LDAPControl **postread_ctrl = NULL;
+ LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
+ int num_ctrls = 0;
+
+ int rc;
+
+ Debug( LDAP_DEBUG_ARGS, "==>" LDAP_XSTRING(ndb_back_modrdn) "(%s,%s,%s)\n",
+ op->o_req_dn.bv_val,op->oq_modrdn.rs_newrdn.bv_val,
+ op->oq_modrdn.rs_newSup ? op->oq_modrdn.rs_newSup->bv_val : "NULL" );
+
+ ctrls[num_ctrls] = NULL;
+
+ slap_mods_opattrs( op, &op->orr_modlist, 1 );
+
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+
+ /* Get our NDB handle */
+ rs->sr_err = ndb_thread_handle( op, &NA.ndb );
+ rdns.nr_num = 0;
+ NA.rdns = &rdns;
+ NA.e = &e;
+ NA2.ndb = NA.ndb;
+ NA2.e = &e2;
+ NA2.rdns = &rdn2;
+
+ if( 0 ) {
+retry: /* transaction retry */
+ NA.txn->close();
+ NA.txn = NULL;
+ if ( e.e_attrs ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+ Debug( LDAP_DEBUG_TRACE, "==>" LDAP_XSTRING(ndb_back_modrdn)
+ ": retrying...\n", 0, 0, 0 );
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ goto return_results;
+ }
+ if ( NA2.ocs ) {
+ ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx );
+ }
+ if ( NA.ocs ) {
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ }
+ ndb_trans_backoff( ++num_retries );
+ }
+ NA.ocs = NULL;
+ NA2.ocs = NULL;
+
+ /* begin transaction */
+ NA.txn = NA.ndb->startTransaction();
+ rs->sr_text = NULL;
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ NA2.txn = NA.txn;
+
+ /* get entry */
+ rs->sr_err = ndb_entry_get_info( op, &NA, 1, &matched );
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ Debug( LDAP_DEBUG_ARGS,
+ "<=- ndb_back_modrdn: no such object %s\n",
+ op->o_req_dn.bv_val, 0, 0 );
+ rs->sr_matched = matched.bv_val;
+ if ( NA.ocs )
+ ndb_check_referral( op, rs, &NA );
+ goto return_results;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+#endif
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+
+ /* acquire and lock entry */
+ rs->sr_err = ndb_entry_get_data( op, &NA, 1 );
+ if ( rs->sr_err )
+ goto return_results;
+
+ if ( !manageDSAit && is_entry_glue( &e )) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto return_results;
+ }
+
+ if ( get_assert( op ) &&
+ ( test_filter( op, &e, (Filter *)get_assertion( op )) != LDAP_COMPARE_TRUE ))
+ {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ goto return_results;
+ }
+
+ /* check write on old entry */
+ rs->sr_err = access_allowed( op, &e, entry, NULL, ACL_WRITE, NULL );
+ if ( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE, "no access to entry\n", 0,
+ 0, 0 );
+ rs->sr_text = "no write access to old entry";
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ /* Can't do it if we have kids */
+ rs->sr_err = ndb_has_children( &NA, &rc );
+ if ( rs->sr_err ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": has_children failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ Debug(LDAP_DEBUG_ARGS,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": non-leaf %s\n",
+ op->o_req_dn.bv_val, 0, 0);
+ rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
+ rs->sr_text = "subtree rename not supported";
+ goto return_results;
+ }
+
+ if (!manageDSAit && is_entry_referral( &e ) ) {
+ /* entry is a referral, don't allow modrdn */
+ rs->sr_ref = get_entry_referrals( op, &e );
+
+ Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn)
+ ": entry %s is referral\n", e.e_dn, 0, 0 );
+
+ rs->sr_err = LDAP_REFERRAL,
+ rs->sr_matched = op->o_req_dn.bv_val;
+ rs->sr_flags = REP_REF_MUSTBEFREED;
+ goto return_results;
+ }
+
+ if ( be_issuffix( op->o_bd, &e.e_nname ) ) {
+ /* There can only be one suffix entry */
+ rs->sr_err = LDAP_NAMING_VIOLATION;
+ rs->sr_text = "cannot rename suffix entry";
+ goto return_results;
+ } else {
+ dnParent( &e.e_nname, &e2.e_nname );
+ dnParent( &e.e_name, &e2.e_name );
+ }
+
+ /* check parent for "children" acl */
+ rs->sr_err = access_allowed( op, &e2,
+ children, NULL,
+ op->oq_modrdn.rs_newSup == NULL ?
+ ACL_WRITE : ACL_WDEL,
+ NULL );
+
+ if ( ! rs->sr_err ) {
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ Debug( LDAP_DEBUG_TRACE, "no access to parent\n", 0,
+ 0, 0 );
+ rs->sr_text = "no write access to old parent's children";
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn) ": wr to children "
+ "of entry %s OK\n", e2.e_name.bv_val, 0, 0 );
+
+ if ( op->oq_modrdn.rs_newSup != NULL ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": new parent \"%s\" requested...\n",
+ op->oq_modrdn.rs_newSup->bv_val, 0, 0 );
+
+ /* newSuperior == oldParent? */
+ if( dn_match( &e2.e_nname, op->oq_modrdn.rs_nnewSup ) ) {
+ Debug( LDAP_DEBUG_TRACE, "bdb_back_modrdn: "
+ "new parent \"%s\" same as the old parent \"%s\"\n",
+ op->oq_modrdn.rs_newSup->bv_val, e2.e_name.bv_val, 0 );
+ op->oq_modrdn.rs_newSup = NULL; /* ignore newSuperior */
+ }
+ }
+
+ if ( op->oq_modrdn.rs_newSup != NULL ) {
+ if ( op->oq_modrdn.rs_newSup->bv_len ) {
+ rdn2.nr_num = 0;
+ np_dn = op->oq_modrdn.rs_newSup;
+ np_ndn = op->oq_modrdn.rs_nnewSup;
+
+ /* newSuperior == oldParent? - checked above */
+ /* newSuperior == entry being moved?, if so ==> ERROR */
+ if ( dnIsSuffix( np_ndn, &e.e_nname )) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ rs->sr_text = "new superior not found";
+ goto return_results;
+ }
+ /* Get Entry with dn=newSuperior. Does newSuperior exist? */
+
+ e2.e_name = *np_dn;
+ e2.e_nname = *np_ndn;
+ rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL );
+ switch( rs->sr_err ) {
+ case 0:
+ break;
+ case LDAP_NO_SUCH_OBJECT:
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": newSup(ndn=%s) not here!\n",
+ np_ndn->bv_val, 0, 0);
+ rs->sr_text = "new superior not found";
+ goto return_results;
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+#endif
+ case LDAP_BUSY:
+ rs->sr_text = "ldap server busy";
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ if ( NA2.ocs ) {
+ Attribute a;
+ int i;
+
+ for ( i=0; !BER_BVISNULL( &NA2.ocs[i] ); i++);
+ a.a_numvals = i;
+ a.a_desc = slap_schema.si_ad_objectClass;
+ a.a_vals = NA2.ocs;
+ a.a_nvals = NA2.ocs;
+ a.a_next = NULL;
+ e2.e_attrs = &a;
+
+ if ( is_entry_alias( &e2 )) {
+ /* parent is an alias, don't allow move */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": entry is alias\n",
+ 0, 0, 0 );
+ rs->sr_text = "new superior is an alias";
+ rs->sr_err = LDAP_ALIAS_PROBLEM;
+ goto return_results;
+ }
+
+ if ( is_entry_referral( &e2 ) ) {
+ /* parent is a referral, don't allow move */
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": entry is referral\n",
+ 0, 0, 0 );
+ rs->sr_text = "new superior is a referral";
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ }
+ }
+
+ /* check newSuperior for "children" acl */
+ rs->sr_err = access_allowed( op, &e2, children,
+ NULL, ACL_WADD, NULL );
+ if( ! rs->sr_err ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": no wr to newSup children\n",
+ 0, 0, 0 );
+ rs->sr_text = "no write access to new superior's children";
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ goto return_results;
+ }
+
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": wr to new parent OK id=%ld\n",
+ (long) e2.e_id, 0, 0 );
+ }
+
+ /* Build target dn and make sure target entry doesn't exist already. */
+ if (!new_dn.bv_val) {
+ build_new_dn( &new_dn, &e2.e_name, &op->oq_modrdn.rs_newrdn, NULL );
+ }
+
+ if (!new_ndn.bv_val) {
+ build_new_dn( &new_ndn, &e2.e_nname, &op->oq_modrdn.rs_nnewrdn, NULL );
+ }
+
+ Debug( LDAP_DEBUG_TRACE, LDAP_XSTRING(ndb_back_modrdn) ": new ndn=%s\n",
+ new_ndn.bv_val, 0, 0 );
+
+ /* Allow rename to same DN */
+ if ( !bvmatch ( &new_ndn, &e.e_nname )) {
+ rdn2.nr_num = 0;
+ e2.e_name = new_dn;
+ e2.e_nname = new_ndn;
+ NA2.ocs = &matched;
+ rs->sr_err = ndb_entry_get_info( op, &NA2, 1, NULL );
+ NA2.ocs = NULL;
+ switch( rs->sr_err ) {
+#if 0
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+#endif
+ case LDAP_NO_SUCH_OBJECT:
+ break;
+ case 0:
+ rs->sr_err = LDAP_ALREADY_EXISTS;
+ goto return_results;
+ default:
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto return_results;
+ }
+ }
+
+ assert( op->orr_modlist != NULL );
+
+ if( op->o_preread ) {
+ if( preread_ctrl == NULL ) {
+ preread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &e,
+ &slap_pre_read_bv, preread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": pre-read failed!\n", 0, 0, 0 );
+ if ( op->o_preread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ /* delete old DN */
+ rs->sr_err = ndb_entry_del_info( op->o_bd, &NA );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": dn2id del failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+#if 0
+ switch( rs->sr_err ) {
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+ }
+#endif
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "DN index delete fail";
+ goto return_results;
+ }
+
+ /* copy entry fields */
+ e2.e_attrs = e.e_attrs;
+ e2.e_id = e.e_id;
+
+ /* add new DN */
+ rs->sr_err = ndb_entry_put_info( op->o_bd, &NA2, 0 );
+ if ( rs->sr_err != 0 ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": dn2id add failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+#if 0
+ switch( rs->sr_err ) {
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+ }
+#endif
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "DN index add failed";
+ goto return_results;
+ }
+
+ /* modify entry */
+ rs->sr_err = ndb_modify_internal( op, &NA2,
+ &rs->sr_text, textbuf, textlen );
+ if( rs->sr_err != LDAP_SUCCESS ) {
+ Debug(LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": modify failed: %s (%d)\n",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code, 0 );
+#if 0
+ switch( rs->sr_err ) {
+ case DB_LOCK_DEADLOCK:
+ case DB_LOCK_NOTGRANTED:
+ goto retry;
+ }
+#endif
+ goto return_results;
+ }
+
+ e.e_attrs = e2.e_attrs;
+
+ if( op->o_postread ) {
+ if( postread_ctrl == NULL ) {
+ postread_ctrl = &ctrls[num_ctrls++];
+ ctrls[num_ctrls] = NULL;
+ }
+ if( slap_read_controls( op, rs, &e2,
+ &slap_post_read_bv, postread_ctrl ) )
+ {
+ Debug( LDAP_DEBUG_TRACE,
+ "<=- " LDAP_XSTRING(ndb_back_modrdn)
+ ": post-read failed!\n", 0, 0, 0 );
+ if ( op->o_postread & SLAP_CONTROL_CRITICAL ) {
+ /* FIXME: is it correct to abort
+ * operation if control fails? */
+ goto return_results;
+ }
+ }
+ }
+
+ if( op->o_noop ) {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Rollback,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn_abort (no-op) failed";
+ } else {
+ rs->sr_err = LDAP_X_NO_OPERATION;
+ }
+ } else {
+ if (( rs->sr_err=NA.txn->execute( NdbTransaction::Commit,
+ NdbOperation::AbortOnError, 1 )) != 0 ) {
+ rs->sr_text = "txn_commit failed";
+ } else {
+ rs->sr_err = LDAP_SUCCESS;
+ }
+ }
+
+ if( rs->sr_err != LDAP_SUCCESS && rs->sr_err != LDAP_X_NO_OPERATION ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn) ": txn_%s failed: %s (%d)\n",
+ op->o_noop ? "abort (no-op)" : "commit",
+ NA.txn->getNdbError().message, NA.txn->getNdbError().code );
+ rs->sr_err = LDAP_OTHER;
+ goto return_results;
+ }
+ NA.txn->close();
+ NA.txn = NULL;
+
+ Debug(LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_modrdn)
+ ": rdn modified%s id=%08lx dn=\"%s\"\n",
+ op->o_noop ? " (no-op)" : "",
+ e.e_id, op->o_req_dn.bv_val );
+
+ rs->sr_err = LDAP_SUCCESS;
+ rs->sr_text = NULL;
+ if( num_ctrls ) rs->sr_ctrls = ctrls;
+
+return_results:
+ if ( NA2.ocs ) {
+ ber_bvarray_free_x( NA2.ocs, op->o_tmpmemctx );
+ NA2.ocs = NULL;
+ }
+
+ if ( NA.ocs ) {
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ NA.ocs = NULL;
+ }
+
+ if ( e.e_attrs ) {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ }
+
+ if( NA.txn != NULL ) {
+ NA.txn->execute( Rollback );
+ NA.txn->close();
+ }
+
+ send_ldap_result( op, rs );
+ slap_graduate_commit_csn( op );
+
+ if( new_dn.bv_val != NULL ) free( new_dn.bv_val );
+ if( new_ndn.bv_val != NULL ) free( new_ndn.bv_val );
+
+ if( preread_ctrl != NULL && (*preread_ctrl) != NULL ) {
+ slap_sl_free( (*preread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *preread_ctrl, op->o_tmpmemctx );
+ }
+ if( postread_ctrl != NULL && (*postread_ctrl) != NULL ) {
+ slap_sl_free( (*postread_ctrl)->ldctl_value.bv_val, op->o_tmpmemctx );
+ slap_sl_free( *postread_ctrl, op->o_tmpmemctx );
+ }
+
+ rs->sr_text = NULL;
+ return rs->sr_err;
+}
diff --git a/servers/slapd/back-ndb/ndbio.cpp b/servers/slapd/back-ndb/ndbio.cpp
new file mode 100644
index 0000000..53559c5
--- /dev/null
+++ b/servers/slapd/back-ndb/ndbio.cpp
@@ -0,0 +1,1677 @@
+/* ndbio.cpp - get/set/del data for NDB */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+#include <lutil.h>
+
+#include "back-ndb.h"
+
+/* For reference only */
+typedef struct MedVar {
+ Int16 len; /* length is always little-endian */
+ char buf[1024];
+} MedVar;
+
+extern "C" {
+ static int ndb_name_cmp( const void *v1, const void *v2 );
+ static int ndb_oc_dup_err( void *v1, void *v2 );
+};
+
+static int
+ndb_name_cmp( const void *v1, const void *v2 )
+{
+ NdbOcInfo *oc1 = (NdbOcInfo *)v1, *oc2 = (NdbOcInfo *)v2;
+ return ber_bvstrcasecmp( &oc1->no_name, &oc2->no_name );
+}
+
+static int
+ndb_oc_dup_err( void *v1, void *v2 )
+{
+ NdbOcInfo *oc = (NdbOcInfo *)v2;
+
+ oc->no_oc = (ObjectClass *)v1;
+ return -1;
+}
+
+/* Find an existing NdbAttrInfo */
+extern "C" NdbAttrInfo *
+ndb_ai_find( struct ndb_info *ni, AttributeType *at )
+{
+ NdbAttrInfo atmp;
+ atmp.na_name = at->sat_cname;
+
+ return (NdbAttrInfo *)avl_find( ni->ni_ai_tree, &atmp, ndb_name_cmp );
+}
+
+/* Find or create an NdbAttrInfo */
+extern "C" NdbAttrInfo *
+ndb_ai_get( struct ndb_info *ni, struct berval *aname )
+{
+ NdbAttrInfo atmp, *ai;
+ atmp.na_name = *aname;
+
+ ai = (NdbAttrInfo *)avl_find( ni->ni_ai_tree, &atmp, ndb_name_cmp );
+ if ( !ai ) {
+ const char *text;
+ AttributeDescription *ad = NULL;
+
+ if ( slap_bv2ad( aname, &ad, &text ))
+ return NULL;
+
+ ai = (NdbAttrInfo *)ch_malloc( sizeof( NdbAttrInfo ));
+ ai->na_desc = ad;
+ ai->na_attr = ai->na_desc->ad_type;
+ ai->na_name = ai->na_attr->sat_cname;
+ ai->na_oi = NULL;
+ ai->na_flag = 0;
+ ai->na_ixcol = 0;
+ ai->na_len = ai->na_attr->sat_atype.at_syntax_len;
+ /* Reasonable default */
+ if ( !ai->na_len ) {
+ if ( ai->na_attr->sat_syntax == slap_schema.si_syn_distinguishedName )
+ ai->na_len = 1024;
+ else
+ ai->na_len = 128;
+ }
+ /* Arbitrary limit */
+ if ( ai->na_len > 1024 )
+ ai->na_len = 1024;
+ avl_insert( &ni->ni_ai_tree, ai, ndb_name_cmp, avl_dup_error );
+ }
+ return ai;
+}
+
+static int
+ndb_ai_check( struct ndb_info *ni, NdbOcInfo *oci, AttributeType **attrs, char **ptr, int *col,
+ int create )
+{
+ NdbAttrInfo *ai;
+ int i;
+
+ for ( i=0; attrs[i]; i++ ) {
+ if ( attrs[i] == slap_schema.si_ad_objectClass->ad_type )
+ continue;
+ /* skip attrs that are in a superior */
+ if ( oci->no_oc && oci->no_oc->soc_sups ) {
+ int j, k, found=0;
+ ObjectClass *oc;
+ for ( j=0; oci->no_oc->soc_sups[j]; j++ ) {
+ oc = oci->no_oc->soc_sups[j];
+ if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT )
+ continue;
+ if ( oc->soc_required ) {
+ for ( k=0; oc->soc_required[k]; k++ ) {
+ if ( attrs[i] == oc->soc_required[k] ) {
+ found = 1;
+ break;
+ }
+ }
+ if ( found ) break;
+ }
+ if ( oc->soc_allowed ) {
+ for ( k=0; oc->soc_allowed[k]; k++ ) {
+ if ( attrs[i] == oc->soc_allowed[k] ) {
+ found = 1;
+ break;
+ }
+ }
+ if ( found ) break;
+ }
+ }
+ if ( found )
+ continue;
+ }
+
+ ai = ndb_ai_get( ni, &attrs[i]->sat_cname );
+ if ( !ai ) {
+ /* can never happen */
+ return LDAP_OTHER;
+ }
+
+ /* An attrset may have already been connected */
+ if (( oci->no_flag & NDB_INFO_ATSET ) && ai->na_oi == oci )
+ continue;
+
+ /* An indexed attr is defined before its OC is */
+ if ( !ai->na_oi ) {
+ ai->na_oi = oci;
+ ai->na_column = (*col)++;
+ }
+
+ oci->no_attrs[oci->no_nattrs++] = ai;
+
+ /* An attrset attr may already be defined */
+ if ( ai->na_oi != oci ) {
+ int j;
+ for ( j=0; j<oci->no_nsets; j++ )
+ if ( oci->no_sets[j] == ai->na_oi ) break;
+ if ( j >= oci->no_nsets ) {
+ /* FIXME: data loss if more sets are in use */
+ if ( oci->no_nsets < NDB_MAX_OCSETS ) {
+ oci->no_sets[oci->no_nsets++] = ai->na_oi;
+ }
+ }
+ continue;
+ }
+
+ if ( create ) {
+ if ( ai->na_flag & NDB_INFO_ATBLOB ) {
+ *ptr += sprintf( *ptr, ", `%s` BLOB", ai->na_attr->sat_cname.bv_val );
+ } else {
+ *ptr += sprintf( *ptr, ", `%s` VARCHAR(%d)", ai->na_attr->sat_cname.bv_val,
+ ai->na_len );
+ }
+ }
+ }
+ return 0;
+}
+
+static int
+ndb_oc_create( struct ndb_info *ni, NdbOcInfo *oci, int create )
+{
+ char buf[4096], *ptr;
+ int i, rc = 0, col;
+
+ if ( create ) {
+ ptr = buf + sprintf( buf,
+ "CREATE TABLE `%s` (eid bigint unsigned NOT NULL, vid int unsigned NOT NULL",
+ oci->no_table.bv_val );
+ }
+
+ col = 0;
+ if ( oci->no_oc->soc_required ) {
+ for ( i=0; oci->no_oc->soc_required[i]; i++ );
+ col += i;
+ }
+ if ( oci->no_oc->soc_allowed ) {
+ for ( i=0; oci->no_oc->soc_allowed[i]; i++ );
+ col += i;
+ }
+ /* assume all are present */
+ oci->no_attrs = (struct ndb_attrinfo **)ch_malloc( col * sizeof(struct ndb_attrinfo *));
+
+ col = 2;
+ ldap_pvt_thread_rdwr_wlock( &ni->ni_ai_rwlock );
+ if ( oci->no_oc->soc_required ) {
+ rc = ndb_ai_check( ni, oci, oci->no_oc->soc_required, &ptr, &col, create );
+ }
+ if ( !rc && oci->no_oc->soc_allowed ) {
+ rc = ndb_ai_check( ni, oci, oci->no_oc->soc_allowed, &ptr, &col, create );
+ }
+ ldap_pvt_thread_rdwr_wunlock( &ni->ni_ai_rwlock );
+
+ /* shrink down to just the needed size */
+ oci->no_attrs = (struct ndb_attrinfo **)ch_realloc( oci->no_attrs,
+ oci->no_nattrs * sizeof(struct ndb_attrinfo *));
+
+ if ( create ) {
+ ptr = lutil_strcopy( ptr, ", PRIMARY KEY(eid, vid) ) ENGINE=ndb PARTITION BY KEY(eid)" );
+ rc = mysql_real_query( &ni->ni_sql, buf, ptr - buf );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ndb_oc_create: CREATE TABLE %s failed, %s (%d)\n",
+ oci->no_table.bv_val, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ }
+ }
+ return rc;
+}
+
+/* Read table definitions from the DB and populate ObjectClassInfo */
+extern "C" int
+ndb_oc_read( struct ndb_info *ni, const NdbDictionary::Dictionary *myDict )
+{
+ const NdbDictionary::Table *myTable;
+ const NdbDictionary::Column *myCol;
+ NdbOcInfo *oci, octmp;
+ NdbAttrInfo *ai;
+ ObjectClass *oc;
+ NdbDictionary::Dictionary::List myList;
+ struct berval bv;
+ int i, j, rc, col;
+
+ rc = myDict->listObjects( myList, NdbDictionary::Object::UserTable );
+ /* Populate our objectClass structures */
+ for ( i=0; i<myList.count; i++ ) {
+ /* Ignore other DBs */
+ if ( strcmp( myList.elements[i].database, ni->ni_dbname ))
+ continue;
+ /* Ignore internal tables */
+ if ( !strncmp( myList.elements[i].name, "OL_", 3 ))
+ continue;
+ ber_str2bv( myList.elements[i].name, 0, 0, &octmp.no_name );
+ oci = (NdbOcInfo *)avl_find( ni->ni_oc_tree, &octmp, ndb_name_cmp );
+ if ( oci )
+ continue;
+
+ oc = oc_bvfind( &octmp.no_name );
+ if ( !oc ) {
+ /* undefined - shouldn't happen */
+ continue;
+ }
+ myTable = myDict->getTable( myList.elements[i].name );
+ oci = (NdbOcInfo *)ch_malloc( sizeof( NdbOcInfo )+oc->soc_cname.bv_len+1 );
+ oci->no_table.bv_val = (char *)(oci+1);
+ strcpy( oci->no_table.bv_val, oc->soc_cname.bv_val );
+ oci->no_table.bv_len = oc->soc_cname.bv_len;
+ oci->no_name = oci->no_table;
+ oci->no_oc = oc;
+ oci->no_flag = 0;
+ oci->no_nsets = 0;
+ oci->no_nattrs = 0;
+ col = 0;
+ /* Make space for all attrs, even tho sups will be dropped */
+ if ( oci->no_oc->soc_required ) {
+ for ( j=0; oci->no_oc->soc_required[j]; j++ );
+ col = j;
+ }
+ if ( oci->no_oc->soc_allowed ) {
+ for ( j=0; oci->no_oc->soc_allowed[j]; j++ );
+ col += j;
+ }
+ oci->no_attrs = (struct ndb_attrinfo **)ch_malloc( col * sizeof(struct ndb_attrinfo *));
+ avl_insert( &ni->ni_oc_tree, oci, ndb_name_cmp, avl_dup_error );
+
+ col = myTable->getNoOfColumns();
+ /* Skip 0 and 1, eid and vid */
+ for ( j = 2; j<col; j++ ) {
+ myCol = myTable->getColumn( j );
+ ber_str2bv( myCol->getName(), 0, 0, &bv );
+ ai = ndb_ai_get( ni, &bv );
+ /* shouldn't happen */
+ if ( !ai )
+ continue;
+ ai->na_oi = oci;
+ ai->na_column = j;
+ ai->na_len = myCol->getLength();
+ if ( myCol->getType() == NdbDictionary::Column::Blob )
+ ai->na_flag |= NDB_INFO_ATBLOB;
+ }
+ }
+ /* Link to any attrsets */
+ for ( i=0; i<myList.count; i++ ) {
+ /* Ignore other DBs */
+ if ( strcmp( myList.elements[i].database, ni->ni_dbname ))
+ continue;
+ /* Ignore internal tables */
+ if ( !strncmp( myList.elements[i].name, "OL_", 3 ))
+ continue;
+ ber_str2bv( myList.elements[i].name, 0, 0, &octmp.no_name );
+ oci = (NdbOcInfo *)avl_find( ni->ni_oc_tree, &octmp, ndb_name_cmp );
+ /* shouldn't happen */
+ if ( !oci )
+ continue;
+ col = 2;
+ if ( oci->no_oc->soc_required ) {
+ rc = ndb_ai_check( ni, oci, oci->no_oc->soc_required, NULL, &col, 0 );
+ }
+ if ( oci->no_oc->soc_allowed ) {
+ rc = ndb_ai_check( ni, oci, oci->no_oc->soc_allowed, NULL, &col, 0 );
+ }
+ /* shrink down to just the needed size */
+ oci->no_attrs = (struct ndb_attrinfo **)ch_realloc( oci->no_attrs,
+ oci->no_nattrs * sizeof(struct ndb_attrinfo *));
+ }
+ return 0;
+}
+
+static int
+ndb_oc_list( struct ndb_info *ni, const NdbDictionary::Dictionary *myDict,
+ struct berval *oname, int implied, NdbOcs *out )
+{
+ const NdbDictionary::Table *myTable;
+ NdbOcInfo *oci, octmp;
+ ObjectClass *oc;
+ int i, rc;
+
+ /* shortcut top */
+ if ( ber_bvstrcasecmp( oname, &slap_schema.si_oc_top->soc_cname )) {
+ octmp.no_name = *oname;
+ oci = (NdbOcInfo *)avl_find( ni->ni_oc_tree, &octmp, ndb_name_cmp );
+ if ( oci ) {
+ oc = oci->no_oc;
+ } else {
+ oc = oc_bvfind( oname );
+ if ( !oc ) {
+ /* undefined - shouldn't happen */
+ return LDAP_INVALID_SYNTAX;
+ }
+ }
+ if ( oc->soc_sups ) {
+ int i;
+
+ for ( i=0; oc->soc_sups[i]; i++ ) {
+ rc = ndb_oc_list( ni, myDict, &oc->soc_sups[i]->soc_cname, 1, out );
+ if ( rc ) return rc;
+ }
+ }
+ } else {
+ oc = slap_schema.si_oc_top;
+ }
+ /* Only insert once */
+ for ( i=0; i<out->no_ntext; i++ )
+ if ( out->no_text[i].bv_val == oc->soc_cname.bv_val )
+ break;
+ if ( i == out->no_ntext ) {
+ for ( i=0; i<out->no_nitext; i++ )
+ if ( out->no_itext[i].bv_val == oc->soc_cname.bv_val )
+ break;
+ if ( i == out->no_nitext ) {
+ if ( implied )
+ out->no_itext[out->no_nitext++] = oc->soc_cname;
+ else
+ out->no_text[out->no_ntext++] = oc->soc_cname;
+ }
+ }
+
+ /* ignore top, etc... */
+ if ( oc->soc_kind == LDAP_SCHEMA_ABSTRACT )
+ return 0;
+
+ if ( !oci ) {
+ ldap_pvt_thread_rdwr_runlock( &ni->ni_oc_rwlock );
+ oci = (NdbOcInfo *)ch_malloc( sizeof( NdbOcInfo )+oc->soc_cname.bv_len+1 );
+ oci->no_table.bv_val = (char *)(oci+1);
+ strcpy( oci->no_table.bv_val, oc->soc_cname.bv_val );
+ oci->no_table.bv_len = oc->soc_cname.bv_len;
+ oci->no_name = oci->no_table;
+ oci->no_oc = oc;
+ oci->no_flag = 0;
+ oci->no_nsets = 0;
+ oci->no_nattrs = 0;
+ ldap_pvt_thread_rdwr_wlock( &ni->ni_oc_rwlock );
+ if ( avl_insert( &ni->ni_oc_tree, oci, ndb_name_cmp, ndb_oc_dup_err )) {
+ octmp.no_oc = oci->no_oc;
+ ch_free( oci );
+ oci = (NdbOcInfo *)octmp.no_oc;
+ }
+ /* see if the oc table already exists in the DB */
+ myTable = myDict->getTable( oci->no_table.bv_val );
+ rc = ndb_oc_create( ni, oci, myTable == NULL );
+ ldap_pvt_thread_rdwr_wunlock( &ni->ni_oc_rwlock );
+ ldap_pvt_thread_rdwr_rlock( &ni->ni_oc_rwlock );
+ if ( rc ) return rc;
+ }
+ /* Only insert once */
+ for ( i=0; i<out->no_ninfo; i++ )
+ if ( out->no_info[i] == oci )
+ break;
+ if ( i == out->no_ninfo )
+ out->no_info[out->no_ninfo++] = oci;
+ return 0;
+}
+
+extern "C" int
+ndb_aset_get( struct ndb_info *ni, struct berval *sname, struct berval *attrs, NdbOcInfo **ret )
+{
+ NdbOcInfo *oci, octmp;
+ int i, rc;
+
+ octmp.no_name = *sname;
+ oci = (NdbOcInfo *)avl_find( ni->ni_oc_tree, &octmp, ndb_name_cmp );
+ if ( oci )
+ return LDAP_ALREADY_EXISTS;
+
+ for ( i=0; !BER_BVISNULL( &attrs[i] ); i++ ) {
+ if ( !at_bvfind( &attrs[i] ))
+ return LDAP_NO_SUCH_ATTRIBUTE;
+ }
+ i++;
+
+ oci = (NdbOcInfo *)ch_calloc( 1, sizeof( NdbOcInfo ) + sizeof( ObjectClass ) +
+ i*sizeof(AttributeType *) + sname->bv_len+1 );
+ oci->no_oc = (ObjectClass *)(oci+1);
+ oci->no_oc->soc_required = (AttributeType **)(oci->no_oc+1);
+ oci->no_table.bv_val = (char *)(oci->no_oc->soc_required+i);
+
+ for ( i=0; !BER_BVISNULL( &attrs[i] ); i++ )
+ oci->no_oc->soc_required[i] = at_bvfind( &attrs[i] );
+
+ strcpy( oci->no_table.bv_val, sname->bv_val );
+ oci->no_table.bv_len = sname->bv_len;
+ oci->no_name = oci->no_table;
+ oci->no_oc->soc_cname = oci->no_name;
+ oci->no_flag = NDB_INFO_ATSET;
+
+ if ( !ber_bvcmp( sname, &slap_schema.si_oc_extensibleObject->soc_cname ))
+ oci->no_oc->soc_kind = slap_schema.si_oc_extensibleObject->soc_kind;
+
+ rc = ndb_oc_create( ni, oci, 0 );
+ if ( !rc )
+ rc = avl_insert( &ni->ni_oc_tree, oci, ndb_name_cmp, avl_dup_error );
+ if ( rc ) {
+ ch_free( oci );
+ } else {
+ *ret = oci;
+ }
+ return rc;
+}
+
+extern "C" int
+ndb_aset_create( struct ndb_info *ni, NdbOcInfo *oci )
+{
+ char buf[4096], *ptr;
+ NdbAttrInfo *ai;
+ int i;
+
+ ptr = buf + sprintf( buf,
+ "CREATE TABLE IF NOT EXISTS `%s` (eid bigint unsigned NOT NULL, vid int unsigned NOT NULL",
+ oci->no_table.bv_val );
+
+ for ( i=0; i<oci->no_nattrs; i++ ) {
+ if ( oci->no_attrs[i]->na_oi != oci )
+ continue;
+ ai = oci->no_attrs[i];
+ ptr += sprintf( ptr, ", `%s` VARCHAR(%d)", ai->na_attr->sat_cname.bv_val,
+ ai->na_len );
+ if ( ai->na_flag & NDB_INFO_INDEX ) {
+ ptr += sprintf( ptr, ", INDEX (`%s`)", ai->na_attr->sat_cname.bv_val );
+ }
+ }
+ ptr = lutil_strcopy( ptr, ", PRIMARY KEY(eid, vid) ) ENGINE=ndb PARTITION BY KEY(eid)" );
+ i = mysql_real_query( &ni->ni_sql, buf, ptr - buf );
+ if ( i ) {
+ Debug( LDAP_DEBUG_ANY,
+ "ndb_aset_create: CREATE TABLE %s failed, %s (%d)\n",
+ oci->no_table.bv_val, mysql_error(&ni->ni_sql), mysql_errno(&ni->ni_sql) );
+ }
+ return i;
+}
+
+static int
+ndb_oc_check( BackendDB *be, Ndb *ndb,
+ struct berval *ocsin, NdbOcs *out )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
+
+ int i, rc = 0;
+
+ out->no_ninfo = 0;
+ out->no_ntext = 0;
+ out->no_nitext = 0;
+
+ /* Find all objectclasses and their superiors. List
+ * the superiors first.
+ */
+
+ ldap_pvt_thread_rdwr_rlock( &ni->ni_oc_rwlock );
+ for ( i=0; !BER_BVISNULL( &ocsin[i] ); i++ ) {
+ rc = ndb_oc_list( ni, myDict, &ocsin[i], 0, out );
+ if ( rc ) break;
+ }
+ ldap_pvt_thread_rdwr_runlock( &ni->ni_oc_rwlock );
+ return rc;
+}
+
+#define V_INS 1
+#define V_DEL 2
+#define V_REP 3
+
+static int ndb_flush_blobs;
+
+/* set all the unique attrs of this objectclass into the table
+ */
+extern "C" int
+ndb_oc_attrs(
+ NdbTransaction *txn,
+ const NdbDictionary::Table *myTable,
+ Entry *e,
+ NdbOcInfo *no,
+ NdbAttrInfo **attrs,
+ int nattrs,
+ Attribute *old
+)
+{
+ char buf[65538], *ptr;
+ Attribute **an, **ao, *a;
+ NdbOperation *myop;
+ int i, j, max = 0;
+ int changed, rc;
+ Uint64 eid = e->e_id;
+
+ if ( !nattrs )
+ return 0;
+
+ an = (Attribute **)ch_malloc( 2 * nattrs * sizeof(Attribute *));
+ ao = an + nattrs;
+
+ /* Turn lists of attrs into arrays for easier access */
+ for ( i=0; i<nattrs; i++ ) {
+ if ( attrs[i]->na_oi != no ) {
+ an[i] = NULL;
+ ao[i] = NULL;
+ continue;
+ }
+ for ( a=e->e_attrs; a; a=a->a_next ) {
+ if ( a->a_desc == slap_schema.si_ad_objectClass )
+ continue;
+ if ( a->a_desc->ad_type == attrs[i]->na_attr ) {
+ /* Don't process same attr twice */
+ if ( a->a_flags & SLAP_ATTR_IXADD )
+ a = NULL;
+ else
+ a->a_flags |= SLAP_ATTR_IXADD;
+ break;
+ }
+ }
+ an[i] = a;
+ if ( a && a->a_numvals > max )
+ max = a->a_numvals;
+ for ( a=old; a; a=a->a_next ) {
+ if ( a->a_desc == slap_schema.si_ad_objectClass )
+ continue;
+ if ( a->a_desc->ad_type == attrs[i]->na_attr )
+ break;
+ }
+ ao[i] = a;
+ if ( a && a->a_numvals > max )
+ max = a->a_numvals;
+ }
+
+ for ( i=0; i<max; i++ ) {
+ myop = NULL;
+ for ( j=0; j<nattrs; j++ ) {
+ if ( !an[j] && !ao[j] )
+ continue;
+ changed = 0;
+ if ( an[j] && an[j]->a_numvals > i ) {
+ /* both old and new are present, compare for changes */
+ if ( ao[j] && ao[j]->a_numvals > i ) {
+ if ( ber_bvcmp( &ao[j]->a_nvals[i], &an[j]->a_nvals[i] ))
+ changed = V_REP;
+ } else {
+ changed = V_INS;
+ }
+ } else {
+ if ( ao[j] && ao[j]->a_numvals > i )
+ changed = V_DEL;
+ }
+ if ( changed ) {
+ if ( !myop ) {
+ rc = LDAP_OTHER;
+ myop = txn->getNdbOperation( myTable );
+ if ( !myop ) {
+ goto done;
+ }
+ if ( old ) {
+ if ( myop->writeTuple()) {
+ goto done;
+ }
+ } else {
+ if ( myop->insertTuple()) {
+ goto done;
+ }
+ }
+ if ( myop->equal( EID_COLUMN, eid )) {
+ goto done;
+ }
+ if ( myop->equal( VID_COLUMN, i )) {
+ goto done;
+ }
+ }
+ if ( attrs[j]->na_flag & NDB_INFO_ATBLOB ) {
+ NdbBlob *myBlob = myop->getBlobHandle( attrs[j]->na_column );
+ rc = LDAP_OTHER;
+ if ( !myBlob ) {
+ Debug( LDAP_DEBUG_TRACE, "ndb_oc_attrs: getBlobHandle failed %s (%d)\n",
+ myop->getNdbError().message, myop->getNdbError().code, 0 );
+ goto done;
+ }
+ if ( slapMode & SLAP_TOOL_MODE )
+ ndb_flush_blobs = 1;
+ if ( changed & V_INS ) {
+ if ( myBlob->setValue( an[j]->a_vals[i].bv_val, an[j]->a_vals[i].bv_len )) {
+ Debug( LDAP_DEBUG_TRACE, "ndb_oc_attrs: blob->setValue failed %s (%d)\n",
+ myBlob->getNdbError().message, myBlob->getNdbError().code, 0 );
+ goto done;
+ }
+ } else {
+ if ( myBlob->setValue( NULL, 0 )) {
+ Debug( LDAP_DEBUG_TRACE, "ndb_oc_attrs: blob->setValue failed %s (%d)\n",
+ myBlob->getNdbError().message, myBlob->getNdbError().code, 0 );
+ goto done;
+ }
+ }
+ } else {
+ if ( changed & V_INS ) {
+ if ( an[j]->a_vals[i].bv_len > attrs[j]->na_len ) {
+ Debug( LDAP_DEBUG_ANY, "ndb_oc_attrs: attribute %s too long for column\n",
+ attrs[j]->na_name.bv_val, 0, 0 );
+ rc = LDAP_CONSTRAINT_VIOLATION;
+ goto done;
+ }
+ ptr = buf;
+ *ptr++ = an[j]->a_vals[i].bv_len & 0xff;
+ if ( attrs[j]->na_len > 255 ) {
+ /* MedVar */
+ *ptr++ = an[j]->a_vals[i].bv_len >> 8;
+ }
+ memcpy( ptr, an[j]->a_vals[i].bv_val, an[j]->a_vals[i].bv_len );
+ ptr = buf;
+ } else {
+ ptr = NULL;
+ }
+ if ( myop->setValue( attrs[j]->na_column, ptr )) {
+ rc = LDAP_OTHER;
+ goto done;
+ }
+ }
+ }
+ }
+ }
+ rc = LDAP_SUCCESS;
+done:
+ ch_free( an );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE, "ndb_oc_attrs: failed %s (%d)\n",
+ myop->getNdbError().message, myop->getNdbError().code, 0 );
+ }
+ return rc;
+}
+
+static int
+ndb_oc_put(
+ const NdbDictionary::Dictionary *myDict,
+ NdbTransaction *txn, NdbOcInfo *no, Entry *e )
+{
+ const NdbDictionary::Table *myTable;
+ int i, rc;
+
+ for ( i=0; i<no->no_nsets; i++ ) {
+ rc = ndb_oc_put( myDict, txn, no->no_sets[i], e );
+ if ( rc )
+ return rc;
+ }
+
+ myTable = myDict->getTable( no->no_table.bv_val );
+ if ( !myTable )
+ return LDAP_OTHER;
+
+ return ndb_oc_attrs( txn, myTable, e, no, no->no_attrs, no->no_nattrs, NULL );
+}
+
+/* This is now only used for Adds. Modifies call ndb_oc_attrs directly. */
+extern "C" int
+ndb_entry_put_data(
+ BackendDB *be,
+ NdbArgs *NA
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ Attribute *aoc;
+ const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+ NdbOcs myOcs;
+ int i, rc;
+
+ /* Get the entry's objectClass attribute */
+ aoc = attr_find( NA->e->e_attrs, slap_schema.si_ad_objectClass );
+ if ( !aoc )
+ return LDAP_OTHER;
+
+ ndb_oc_check( be, NA->ndb, aoc->a_nvals, &myOcs );
+ myOcs.no_info[myOcs.no_ninfo++] = ni->ni_opattrs;
+
+ /* Walk thru objectclasses, find all the attributes belonging to a class */
+ for ( i=0; i<myOcs.no_ninfo; i++ ) {
+ rc = ndb_oc_put( myDict, NA->txn, myOcs.no_info[i], NA->e );
+ if ( rc ) return rc;
+ }
+
+ /* slapadd tries to batch multiple entries per txn, but entry data is
+ * transient and blob data is required to remain valid for the whole txn.
+ * So we need to flush blobs before their source data disappears.
+ */
+ if (( slapMode & SLAP_TOOL_MODE ) && ndb_flush_blobs )
+ NA->txn->execute( NdbTransaction::NoCommit );
+
+ return 0;
+}
+
+static void
+ndb_oc_get( Operation *op, NdbOcInfo *no, int *j, int *nocs, NdbOcInfo ***oclist )
+{
+ int i;
+ NdbOcInfo **ol2;
+
+ for ( i=0; i<no->no_nsets; i++ ) {
+ ndb_oc_get( op, no->no_sets[i], j, nocs, oclist );
+ }
+
+ /* Don't insert twice */
+ ol2 = *oclist;
+ for ( i=0; i<*j; i++ )
+ if ( ol2[i] == no )
+ return;
+
+ if ( *j >= *nocs ) {
+ *nocs *= 2;
+ ol2 = (NdbOcInfo **)op->o_tmprealloc( *oclist, *nocs * sizeof(NdbOcInfo *), op->o_tmpmemctx );
+ *oclist = ol2;
+ }
+ ol2 = *oclist;
+ ol2[(*j)++] = no;
+}
+
+/* Retrieve attribute data for given entry. The entry's DN and eid should
+ * already be populated.
+ */
+extern "C" int
+ndb_entry_get_data(
+ Operation *op,
+ NdbArgs *NA,
+ int update
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+ const NdbDictionary::Table *myTable;
+ NdbIndexScanOperation **myop = NULL;
+ Uint64 eid;
+
+ Attribute *a;
+ NdbOcs myOcs;
+ NdbOcInfo *oci, **oclist = NULL;
+ char abuf[65536], *ptr, **attrs = NULL;
+ struct berval bv[2];
+ int *ocx = NULL;
+
+ /* FIXME: abuf should be dynamically allocated */
+
+ int i, j, k, nocs, nattrs, rc = LDAP_OTHER;
+
+ eid = NA->e->e_id;
+
+ ndb_oc_check( op->o_bd, NA->ndb, NA->ocs, &myOcs );
+ myOcs.no_info[myOcs.no_ninfo++] = ni->ni_opattrs;
+ nocs = myOcs.no_ninfo;
+
+ oclist = (NdbOcInfo **)op->o_tmpcalloc( 1, nocs * sizeof(NdbOcInfo *), op->o_tmpmemctx );
+
+ for ( i=0, j=0; i<myOcs.no_ninfo; i++ ) {
+ ndb_oc_get( op, myOcs.no_info[i], &j, &nocs, &oclist );
+ }
+
+ nocs = j;
+ nattrs = 0;
+ for ( i=0; i<nocs; i++ )
+ nattrs += oclist[i]->no_nattrs;
+
+ ocx = (int *)op->o_tmpalloc( nocs * sizeof(int), op->o_tmpmemctx );
+
+ attrs = (char **)op->o_tmpalloc( nattrs * sizeof(char *), op->o_tmpmemctx );
+
+ myop = (NdbIndexScanOperation **)op->o_tmpalloc( nattrs * sizeof(NdbIndexScanOperation *), op->o_tmpmemctx );
+
+ k = 0;
+ ptr = abuf;
+ for ( i=0; i<nocs; i++ ) {
+ oci = oclist[i];
+
+ myop[i] = NA->txn->getNdbIndexScanOperation( "PRIMARY", oci->no_table.bv_val );
+ if ( !myop[i] )
+ goto leave;
+ if ( myop[i]->readTuples( update ? NdbOperation::LM_Exclusive : NdbOperation::LM_CommittedRead ))
+ goto leave;
+ if ( myop[i]->setBound( 0U, NdbIndexScanOperation::BoundEQ, &eid ))
+ goto leave;
+
+ for ( j=0; j<oci->no_nattrs; j++ ) {
+ if ( oci->no_attrs[j]->na_oi != oci )
+ continue;
+ if ( oci->no_attrs[j]->na_flag & NDB_INFO_ATBLOB ) {
+ NdbBlob *bi = myop[i]->getBlobHandle( oci->no_attrs[j]->na_column );
+ attrs[k++] = (char *)bi;
+ } else {
+ attrs[k] = ptr;
+ *ptr++ = 0;
+ if ( oci->no_attrs[j]->na_len > 255 )
+ *ptr++ = 0;
+ ptr += oci->no_attrs[j]->na_len + 1;
+ myop[i]->getValue( oci->no_attrs[j]->na_column, attrs[k++] );
+ }
+ }
+ ocx[i] = k;
+ }
+ /* Must use IgnoreError, because an entry with multiple objectClasses may not
+ * actually have attributes defined in each class / table.
+ */
+ if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1) < 0 )
+ goto leave;
+
+ /* count results */
+ for ( i=0; i<nocs; i++ ) {
+ if (( j = myop[i]->nextResult(true) )) {
+ if ( j < 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ndb_entry_get_data: first nextResult(%d) failed: %s (%d)\n",
+ i, myop[i]->getNdbError().message, myop[i]->getNdbError().code );
+ }
+ myop[i] = NULL;
+ }
+ }
+
+ nattrs = 0;
+ k = 0;
+ for ( i=0; i<nocs; i++ ) {
+ oci = oclist[i];
+ for ( j=0; j<oci->no_nattrs; j++ ) {
+ unsigned char *buf;
+ int len;
+ if ( oci->no_attrs[j]->na_oi != oci )
+ continue;
+ if ( !myop[i] ) {
+ attrs[k] = NULL;
+ } else if ( oci->no_attrs[j]->na_flag & NDB_INFO_ATBLOB ) {
+ void *vi = attrs[k];
+ NdbBlob *bi = (NdbBlob *)vi;
+ int isNull;
+ bi->getNull( isNull );
+ if ( !isNull ) {
+ nattrs++;
+ } else {
+ attrs[k] = NULL;
+ }
+ } else {
+ buf = (unsigned char *)attrs[k];
+ len = buf[0];
+ if ( oci->no_attrs[j]->na_len > 255 ) {
+ /* MedVar */
+ len |= (buf[1] << 8);
+ }
+ if ( len ) {
+ nattrs++;
+ } else {
+ attrs[k] = NULL;
+ }
+ }
+ k++;
+ }
+ }
+
+ a = attrs_alloc( nattrs+1 );
+ NA->e->e_attrs = a;
+
+ a->a_desc = slap_schema.si_ad_objectClass;
+ a->a_vals = NULL;
+ ber_bvarray_dup_x( &a->a_vals, NA->ocs, NULL );
+ a->a_nvals = a->a_vals;
+ a->a_numvals = myOcs.no_ntext;
+
+ BER_BVZERO( &bv[1] );
+
+ do {
+ a = NA->e->e_attrs->a_next;
+ k = 0;
+ for ( i=0; i<nocs; k=ocx[i], i++ ) {
+ oci = oclist[i];
+ for ( j=0; j<oci->no_nattrs; j++ ) {
+ unsigned char *buf;
+ struct berval nbv;
+ if ( oci->no_attrs[j]->na_oi != oci )
+ continue;
+ buf = (unsigned char *)attrs[k++];
+ if ( !buf )
+ continue;
+ if ( !myop[i] ) {
+ a=a->a_next;
+ continue;
+ }
+ if ( oci->no_attrs[j]->na_flag & NDB_INFO_ATBLOB ) {
+ void *vi = (void *)buf;
+ NdbBlob *bi = (NdbBlob *)vi;
+ Uint64 len;
+ Uint32 len2;
+ int isNull;
+ bi->getNull( isNull );
+ if ( isNull ) {
+ a = a->a_next;
+ continue;
+ }
+ bi->getLength( len );
+ bv[0].bv_len = len;
+ bv[0].bv_val = (char *)ch_malloc( len+1 );
+ len2 = len;
+ if ( bi->readData( bv[0].bv_val, len2 )) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ndb_entry_get_data: blob readData failed: %s (%d), len %d\n",
+ bi->getNdbError().message, bi->getNdbError().code, len2 );
+ }
+ bv[0].bv_val[len] = '\0';
+ ber_bvarray_add_x( &a->a_vals, bv, NULL );
+ } else {
+ bv[0].bv_len = buf[0];
+ if ( oci->no_attrs[j]->na_len > 255 ) {
+ /* MedVar */
+ bv[0].bv_len |= (buf[1] << 8);
+ bv[0].bv_val = (char *)buf+2;
+ buf[1] = 0;
+ } else {
+ bv[0].bv_val = (char *)buf+1;
+ }
+ buf[0] = 0;
+ if ( bv[0].bv_len == 0 ) {
+ a = a->a_next;
+ continue;
+ }
+ bv[0].bv_val[bv[0].bv_len] = '\0';
+ value_add_one( &a->a_vals, bv );
+ }
+ a->a_desc = oci->no_attrs[j]->na_desc;
+ attr_normalize_one( a->a_desc, bv, &nbv, NULL );
+ a->a_numvals++;
+ if ( !BER_BVISNULL( &nbv )) {
+ ber_bvarray_add_x( &a->a_nvals, &nbv, NULL );
+ } else if ( !a->a_nvals ) {
+ a->a_nvals = a->a_vals;
+ }
+ a = a->a_next;
+ }
+ }
+ k = 0;
+ for ( i=0; i<nocs; i++ ) {
+ if ( !myop[i] )
+ continue;
+ if ((j = myop[i]->nextResult(true))) {
+ if ( j < 0 ) {
+ Debug( LDAP_DEBUG_TRACE,
+ "ndb_entry_get_data: last nextResult(%d) failed: %s (%d)\n",
+ i, myop[i]->getNdbError().message, myop[i]->getNdbError().code );
+ }
+ myop[i] = NULL;
+ } else {
+ k = 1;
+ }
+ }
+ } while ( k );
+
+ rc = 0;
+leave:
+ if ( myop ) {
+ op->o_tmpfree( myop, op->o_tmpmemctx );
+ }
+ if ( attrs ) {
+ op->o_tmpfree( attrs, op->o_tmpmemctx );
+ }
+ if ( ocx ) {
+ op->o_tmpfree( ocx, op->o_tmpmemctx );
+ }
+ if ( oclist ) {
+ op->o_tmpfree( oclist, op->o_tmpmemctx );
+ }
+
+ return rc;
+}
+
+static int
+ndb_oc_del(
+ NdbTransaction *txn, Uint64 eid, NdbOcInfo *no )
+{
+ NdbIndexScanOperation *myop;
+ int i, rc;
+
+ for ( i=0; i<no->no_nsets; i++ ) {
+ rc = ndb_oc_del( txn, eid, no->no_sets[i] );
+ if ( rc ) return rc;
+ }
+
+ myop = txn->getNdbIndexScanOperation( "PRIMARY", no->no_table.bv_val );
+ if ( !myop )
+ return LDAP_OTHER;
+ if ( myop->readTuples( NdbOperation::LM_Exclusive ))
+ return LDAP_OTHER;
+ if ( myop->setBound( 0U, NdbIndexScanOperation::BoundEQ, &eid ))
+ return LDAP_OTHER;
+
+ txn->execute(NoCommit);
+ while ( myop->nextResult(true) == 0) {
+ do {
+ myop->deleteCurrentTuple();
+ } while (myop->nextResult(false) == 0);
+ txn->execute(NoCommit);
+ }
+
+ return 0;
+}
+
+extern "C" int
+ndb_entry_del_data(
+ BackendDB *be,
+ NdbArgs *NA
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ Uint64 eid = NA->e->e_id;
+ int i;
+ NdbOcs myOcs;
+
+ ndb_oc_check( be, NA->ndb, NA->ocs, &myOcs );
+ myOcs.no_info[myOcs.no_ninfo++] = ni->ni_opattrs;
+
+ for ( i=0; i<myOcs.no_ninfo; i++ ) {
+ if ( ndb_oc_del( NA->txn, eid, myOcs.no_info[i] ))
+ return LDAP_OTHER;
+ }
+
+ return 0;
+}
+
+extern "C" int
+ndb_dn2rdns(
+ struct berval *dn,
+ NdbRdns *rdns
+)
+{
+ char *beg, *end;
+ int i, len;
+
+ /* Walk thru RDNs */
+ end = dn->bv_val + dn->bv_len;
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ for ( beg = end-1; beg > dn->bv_val; beg-- ) {
+ if (*beg == ',') {
+ beg++;
+ break;
+ }
+ }
+ if ( beg >= dn->bv_val ) {
+ len = end - beg;
+ /* RDN is too long */
+ if ( len > NDB_RDN_LEN )
+ return LDAP_CONSTRAINT_VIOLATION;
+ memcpy( rdns->nr_buf[i]+1, beg, len );
+ } else {
+ break;
+ }
+ rdns->nr_buf[i][0] = len;
+ end = beg - 1;
+ }
+ /* Too many RDNs in DN */
+ if ( i == NDB_MAX_RDNS && beg > dn->bv_val ) {
+ return LDAP_CONSTRAINT_VIOLATION;
+ }
+ rdns->nr_num = i;
+ return 0;
+}
+
+static int
+ndb_rdns2keys(
+ NdbOperation *myop,
+ NdbRdns *rdns
+)
+{
+ int i;
+ char dummy[2] = {0,0};
+
+ /* Walk thru RDNs */
+ for ( i=0; i<rdns->nr_num; i++ ) {
+ if ( myop->equal( i+RDN_COLUMN, rdns->nr_buf[i] ))
+ return LDAP_OTHER;
+ }
+ for ( ; i<NDB_MAX_RDNS; i++ ) {
+ if ( myop->equal( i+RDN_COLUMN, dummy ))
+ return LDAP_OTHER;
+ }
+ return 0;
+}
+
+/* Store the DN2ID_TABLE fields */
+extern "C" int
+ndb_entry_put_info(
+ BackendDB *be,
+ NdbArgs *NA,
+ int update
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+ const NdbDictionary::Table *myTable = myDict->getTable( DN2ID_TABLE );
+ NdbOperation *myop;
+ NdbAttrInfo *ai;
+ Attribute *aoc, *a;
+
+ /* Get the entry's objectClass attribute; it's ok to be
+ * absent on a fresh insert
+ */
+ aoc = attr_find( NA->e->e_attrs, slap_schema.si_ad_objectClass );
+ if ( update && !aoc )
+ return LDAP_OBJECT_CLASS_VIOLATION;
+
+ myop = NA->txn->getNdbOperation( myTable );
+ if ( !myop )
+ return LDAP_OTHER;
+ if ( update ) {
+ if ( myop->updateTuple())
+ return LDAP_OTHER;
+ } else {
+ if ( myop->insertTuple())
+ return LDAP_OTHER;
+ }
+
+ if ( ndb_rdns2keys( myop, NA->rdns ))
+ return LDAP_OTHER;
+
+ /* Set entry ID */
+ {
+ Uint64 eid = NA->e->e_id;
+ if ( myop->setValue( EID_COLUMN, eid ))
+ return LDAP_OTHER;
+ }
+
+ /* Set list of objectClasses */
+ /* List is <sp> <class> <sp> <class> <sp> ... so that
+ * searches for " class " will yield accurate results
+ */
+ if ( aoc ) {
+ char *ptr, buf[sizeof(MedVar)];
+ NdbOcs myOcs;
+ int i;
+
+ ndb_oc_check( be, NA->ndb, aoc->a_nvals, &myOcs );
+ ptr = buf+2;
+ *ptr++ = ' ';
+ for ( i=0; i<myOcs.no_ntext; i++ ) {
+ /* data loss... */
+ if ( ptr + myOcs.no_text[i].bv_len + 1 >= &buf[sizeof(buf)] )
+ break;
+ ptr = lutil_strcopy( ptr, myOcs.no_text[i].bv_val );
+ *ptr++ = ' ';
+ }
+
+ /* implicit classes */
+ if ( myOcs.no_nitext ) {
+ *ptr++ = '@';
+ *ptr++ = ' ';
+ for ( i=0; i<myOcs.no_nitext; i++ ) {
+ /* data loss... */
+ if ( ptr + myOcs.no_itext[i].bv_len + 1 >= &buf[sizeof(buf)] )
+ break;
+ ptr = lutil_strcopy( ptr, myOcs.no_itext[i].bv_val );
+ *ptr++ = ' ';
+ }
+ }
+
+ i = ptr - buf - 2;
+ buf[0] = i & 0xff;
+ buf[1] = i >> 8;
+ if ( myop->setValue( OCS_COLUMN, buf ))
+ return LDAP_OTHER;
+ }
+
+ /* Set any indexed attrs */
+ for ( a = NA->e->e_attrs; a; a=a->a_next ) {
+ ai = ndb_ai_find( ni, a->a_desc->ad_type );
+ if ( ai && ( ai->na_flag & NDB_INFO_INDEX )) {
+ char *ptr, buf[sizeof(MedVar)];
+ int len;
+
+ ptr = buf+1;
+ len = a->a_vals[0].bv_len;
+ /* FIXME: data loss */
+ if ( len > ai->na_len )
+ len = ai->na_len;
+ buf[0] = len & 0xff;
+ if ( ai->na_len > 255 ) {
+ *ptr++ = len >> 8;
+ }
+ memcpy( ptr, a->a_vals[0].bv_val, len );
+ if ( myop->setValue( ai->na_ixcol, buf ))
+ return LDAP_OTHER;
+ }
+ }
+
+ return 0;
+}
+
+extern "C" struct berval *
+ndb_str2bvarray(
+ char *str,
+ int len,
+ char delim,
+ void *ctx
+)
+{
+ struct berval *list, tmp;
+ char *beg;
+ int i, num;
+
+ while ( *str == delim ) {
+ str++;
+ len--;
+ }
+
+ while ( str[len-1] == delim ) {
+ str[--len] = '\0';
+ }
+
+ for ( i = 1, beg = str;; i++ ) {
+ beg = strchr( beg, delim );
+ if ( !beg )
+ break;
+ if ( beg >= str + len )
+ break;
+ beg++;
+ }
+
+ num = i;
+ list = (struct berval *)slap_sl_malloc( (num+1)*sizeof(struct berval), ctx);
+
+ for ( i = 0, beg = str; i<num; i++ ) {
+ tmp.bv_val = beg;
+ beg = strchr( beg, delim );
+ if ( beg >= str + len )
+ beg = NULL;
+ if ( beg ) {
+ tmp.bv_len = beg - tmp.bv_val;
+ } else {
+ tmp.bv_len = len - (tmp.bv_val - str);
+ }
+ ber_dupbv_x( &list[i], &tmp, ctx );
+ beg++;
+ }
+
+ BER_BVZERO( &list[i] );
+ return list;
+}
+
+extern "C" struct berval *
+ndb_ref2oclist(
+ const char *ref,
+ void *ctx
+)
+{
+ char *implied;
+
+ /* MedVar */
+ int len = ref[0] | (ref[1] << 8);
+
+ /* don't return the implied classes */
+ implied = (char *)memchr( ref+2, '@', len );
+ if ( implied ) {
+ len = implied - ref - 2;
+ *implied = '\0';
+ }
+
+ return ndb_str2bvarray( (char *)ref+2, len, ' ', ctx );
+}
+
+/* Retrieve the DN2ID_TABLE fields. Can call with NULL ocs if just verifying
+ * the existence of a DN.
+ */
+extern "C" int
+ndb_entry_get_info(
+ Operation *op,
+ NdbArgs *NA,
+ int update,
+ struct berval *matched
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+ const NdbDictionary::Table *myTable = myDict->getTable( DN2ID_TABLE );
+ NdbOperation *myop[NDB_MAX_RDNS];
+ NdbRecAttr *eid[NDB_MAX_RDNS], *oc[NDB_MAX_RDNS];
+ char idbuf[NDB_MAX_RDNS][2*sizeof(ID)];
+ char ocbuf[NDB_MAX_RDNS][NDB_OC_BUFLEN];
+
+ if ( matched ) {
+ BER_BVZERO( matched );
+ }
+ if ( !myTable ) {
+ return LDAP_OTHER;
+ }
+
+ myop[0] = NA->txn->getNdbOperation( myTable );
+ if ( !myop[0] ) {
+ return LDAP_OTHER;
+ }
+
+ if ( myop[0]->readTuple( update ? NdbOperation::LM_Exclusive : NdbOperation::LM_CommittedRead )) {
+ return LDAP_OTHER;
+ }
+
+ if ( !NA->rdns->nr_num && ndb_dn2rdns( &NA->e->e_name, NA->rdns )) {
+ return LDAP_NO_SUCH_OBJECT;
+ }
+
+ if ( ndb_rdns2keys( myop[0], NA->rdns )) {
+ return LDAP_OTHER;
+ }
+
+ eid[0] = myop[0]->getValue( EID_COLUMN, idbuf[0] );
+ if ( !eid[0] ) {
+ return LDAP_OTHER;
+ }
+
+ ocbuf[0][0] = 0;
+ ocbuf[0][1] = 0;
+ if ( !NA->ocs ) {
+ oc[0] = myop[0]->getValue( OCS_COLUMN, ocbuf[0] );
+ if ( !oc[0] ) {
+ return LDAP_OTHER;
+ }
+ }
+
+ if ( NA->txn->execute(NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1) < 0 ) {
+ return LDAP_OTHER;
+ }
+
+ switch( myop[0]->getNdbError().code ) {
+ case 0:
+ if ( !eid[0]->isNULL() && ( NA->e->e_id = eid[0]->u_64_value() )) {
+ /* If we didn't care about OCs, or we got them */
+ if ( NA->ocs || ocbuf[0][0] || ocbuf[0][1] ) {
+ /* If wanted, return them */
+ if ( !NA->ocs )
+ NA->ocs = ndb_ref2oclist( ocbuf[0], op->o_tmpmemctx );
+ break;
+ }
+ }
+ /* FALLTHRU */
+ case NDB_NO_SUCH_OBJECT: /* no such tuple: look for closest parent */
+ if ( matched ) {
+ int i, j, k;
+ char dummy[2] = {0,0};
+
+ /* get to last RDN, then back up 1 */
+ k = NA->rdns->nr_num - 1;
+
+ for ( i=0; i<k; i++ ) {
+ myop[i] = NA->txn->getNdbOperation( myTable );
+ if ( !myop[i] )
+ return LDAP_OTHER;
+ if ( myop[i]->readTuple( NdbOperation::LM_CommittedRead ))
+ return LDAP_OTHER;
+ for ( j=0; j<=i; j++ ) {
+ if ( myop[i]->equal( j+RDN_COLUMN, NA->rdns->nr_buf[j] ))
+ return LDAP_OTHER;
+ }
+ for ( ;j<NDB_MAX_RDNS; j++ ) {
+ if ( myop[i]->equal( j+RDN_COLUMN, dummy ))
+ return LDAP_OTHER;
+ }
+ eid[i] = myop[i]->getValue( EID_COLUMN, idbuf[i] );
+ if ( !eid[i] ) {
+ return LDAP_OTHER;
+ }
+ ocbuf[i][0] = 0;
+ ocbuf[i][1] = 0;
+ if ( !NA->ocs ) {
+ oc[i] = myop[0]->getValue( OCS_COLUMN, ocbuf[i] );
+ if ( !oc[i] ) {
+ return LDAP_OTHER;
+ }
+ }
+ }
+ if ( NA->txn->execute(NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1) < 0 ) {
+ return LDAP_OTHER;
+ }
+ for ( --i; i>=0; i-- ) {
+ if ( myop[i]->getNdbError().code == 0 ) {
+ for ( j=0; j<=i; j++ )
+ matched->bv_len += NA->rdns->nr_buf[j][0];
+ NA->erdns = NA->rdns->nr_num;
+ NA->rdns->nr_num = j;
+ matched->bv_len += i;
+ matched->bv_val = NA->e->e_name.bv_val +
+ NA->e->e_name.bv_len - matched->bv_len;
+ if ( !eid[i]->isNULL() )
+ NA->e->e_id = eid[i]->u_64_value();
+ if ( !NA->ocs )
+ NA->ocs = ndb_ref2oclist( ocbuf[i], op->o_tmpmemctx );
+ break;
+ }
+ }
+ }
+ return LDAP_NO_SUCH_OBJECT;
+ default:
+ return LDAP_OTHER;
+ }
+
+ return 0;
+}
+
+extern "C" int
+ndb_entry_del_info(
+ BackendDB *be,
+ NdbArgs *NA
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ const NdbDictionary::Dictionary *myDict = NA->ndb->getDictionary();
+ const NdbDictionary::Table *myTable = myDict->getTable( DN2ID_TABLE );
+ NdbOperation *myop;
+
+ myop = NA->txn->getNdbOperation( myTable );
+ if ( !myop )
+ return LDAP_OTHER;
+ if ( myop->deleteTuple())
+ return LDAP_OTHER;
+
+ if ( ndb_rdns2keys( myop, NA->rdns ))
+ return LDAP_OTHER;
+
+ return 0;
+}
+
+extern "C" int
+ndb_next_id(
+ BackendDB *be,
+ Ndb *ndb,
+ ID *id
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
+ const NdbDictionary::Table *myTable = myDict->getTable( NEXTID_TABLE );
+ Uint64 nid = 0;
+ int rc;
+
+ if ( !myTable ) {
+ Debug( LDAP_DEBUG_ANY, "ndb_next_id: " NEXTID_TABLE " table is missing\n",
+ 0, 0, 0 );
+ return LDAP_OTHER;
+ }
+
+ rc = ndb->getAutoIncrementValue( myTable, nid, 1000 );
+ if ( !rc )
+ *id = nid;
+ return rc;
+}
+
+extern "C" { static void ndb_thread_hfree( void *key, void *data ); };
+static void
+ndb_thread_hfree( void *key, void *data )
+{
+ Ndb *ndb = (Ndb *)data;
+ delete ndb;
+}
+
+extern "C" int
+ndb_thread_handle(
+ Operation *op,
+ Ndb **ndb )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ void *data;
+
+ if ( ldap_pvt_thread_pool_getkey( op->o_threadctx, ni, &data, NULL )) {
+ Ndb *myNdb;
+ int rc;
+ ldap_pvt_thread_mutex_lock( &ni->ni_conn_mutex );
+ myNdb = new Ndb( ni->ni_cluster[ni->ni_nextconn++], ni->ni_dbname );
+ if ( ni->ni_nextconn >= ni->ni_nconns )
+ ni->ni_nextconn = 0;
+ ldap_pvt_thread_mutex_unlock( &ni->ni_conn_mutex );
+ if ( !myNdb ) {
+ return LDAP_OTHER;
+ }
+ rc = myNdb->init(1024);
+ if ( rc ) {
+ delete myNdb;
+ Debug( LDAP_DEBUG_ANY, "ndb_thread_handle: err %d\n",
+ rc, 0, 0 );
+ return rc;
+ }
+ data = (void *)myNdb;
+ if (( rc = ldap_pvt_thread_pool_setkey( op->o_threadctx, ni,
+ data, ndb_thread_hfree, NULL, NULL ))) {
+ delete myNdb;
+ Debug( LDAP_DEBUG_ANY, "ndb_thread_handle: err %d\n",
+ rc, 0, 0 );
+ return rc;
+ }
+ }
+ *ndb = (Ndb *)data;
+ return 0;
+}
+
+extern "C" int
+ndb_entry_get(
+ Operation *op,
+ struct berval *ndn,
+ ObjectClass *oc,
+ AttributeDescription *ad,
+ int rw,
+ Entry **ent )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ NdbArgs NA;
+ Entry e = {0};
+ int rc;
+
+ /* Get our NDB handle */
+ rc = ndb_thread_handle( op, &NA.ndb );
+
+ NA.txn = NA.ndb->startTransaction();
+ if( !NA.txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_entry_get) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ return 1;
+ }
+
+ e.e_name = *ndn;
+ NA.e = &e;
+ /* get entry */
+ {
+ NdbRdns rdns;
+ rdns.nr_num = 0;
+ NA.ocs = NULL;
+ NA.rdns = &rdns;
+ rc = ndb_entry_get_info( op, &NA, rw, NULL );
+ }
+ if ( rc == 0 ) {
+ e.e_name = *ndn;
+ e.e_nname = *ndn;
+ rc = ndb_entry_get_data( op, &NA, 0 );
+ ber_bvarray_free( NA.ocs );
+ if ( rc == 0 ) {
+ if ( oc && !is_entry_objectclass_or_sub( &e, oc )) {
+ attrs_free( e.e_attrs );
+ rc = 1;
+ }
+ }
+ }
+ if ( rc == 0 ) {
+ *ent = entry_alloc();
+ **ent = e;
+ ber_dupbv( &(*ent)->e_name, ndn );
+ ber_dupbv( &(*ent)->e_nname, ndn );
+ } else {
+ rc = 1;
+ }
+ NA.txn->close();
+ return rc;
+}
+
+/* Congestion avoidance code
+ * for Deadlock Rollback
+ */
+
+extern "C" void
+ndb_trans_backoff( int num_retries )
+{
+ int i;
+ int delay = 0;
+ int pow_retries = 1;
+ unsigned long key = 0;
+ unsigned long max_key = -1;
+ struct timeval timeout;
+
+ lutil_entropy( (unsigned char *) &key, sizeof( unsigned long ));
+
+ for ( i = 0; i < num_retries; i++ ) {
+ if ( i >= 5 ) break;
+ pow_retries *= 4;
+ }
+
+ delay = 16384 * (key * (double) pow_retries / (double) max_key);
+ delay = delay ? delay : 1;
+
+ Debug( LDAP_DEBUG_TRACE, "delay = %d, num_retries = %d\n", delay, num_retries, 0 );
+
+ timeout.tv_sec = delay / 1000000;
+ timeout.tv_usec = delay % 1000000;
+ select( 0, NULL, NULL, NULL, &timeout );
+}
+
+extern "C" void
+ndb_check_referral( Operation *op, SlapReply *rs, NdbArgs *NA )
+{
+ struct berval dn, ndn;
+ int i, dif;
+ dif = NA->erdns - NA->rdns->nr_num;
+
+ /* Set full DN of matched into entry */
+ for ( i=0; i<dif; i++ ) {
+ dnParent( &NA->e->e_name, &dn );
+ dnParent( &NA->e->e_nname, &ndn );
+ NA->e->e_name = dn;
+ NA->e->e_nname = ndn;
+ }
+
+ /* return referral only if "disclose" is granted on the object */
+ if ( access_allowed( op, NA->e, slap_schema.si_ad_entry,
+ NULL, ACL_DISCLOSE, NULL )) {
+ Attribute a;
+ for ( i=0; !BER_BVISNULL( &NA->ocs[i] ); i++ );
+ a.a_numvals = i;
+ a.a_desc = slap_schema.si_ad_objectClass;
+ a.a_vals = NA->ocs;
+ a.a_nvals = NA->ocs;
+ a.a_next = NULL;
+ NA->e->e_attrs = &a;
+ if ( is_entry_referral( NA->e )) {
+ NA->e->e_attrs = NULL;
+ ndb_entry_get_data( op, NA, 0 );
+ rs->sr_ref = get_entry_referrals( op, NA->e );
+ if ( rs->sr_ref ) {
+ rs->sr_err = LDAP_REFERRAL;
+ rs->sr_flags |= REP_REF_MUSTBEFREED;
+ }
+ attrs_free( NA->e->e_attrs );
+ }
+ NA->e->e_attrs = NULL;
+ }
+}
diff --git a/servers/slapd/back-ndb/proto-ndb.h b/servers/slapd/back-ndb/proto-ndb.h
new file mode 100644
index 0000000..6a6e092
--- /dev/null
+++ b/servers/slapd/back-ndb/proto-ndb.h
@@ -0,0 +1,166 @@
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#ifndef _PROTO_NDB_H
+#define _PROTO_NDB_H
+
+LDAP_BEGIN_DECL
+
+extern BI_init ndb_back_initialize;
+
+extern BI_open ndb_back_open;
+extern BI_close ndb_back_close;
+extern BI_destroy ndb_back_destroy;
+
+extern BI_db_init ndb_back_db_init;
+extern BI_db_destroy ndb_back_db_destroy;
+
+extern BI_op_bind ndb_back_bind;
+extern BI_op_unbind ndb_back_unbind;
+extern BI_op_search ndb_back_search;
+extern BI_op_compare ndb_back_compare;
+extern BI_op_modify ndb_back_modify;
+extern BI_op_modrdn ndb_back_modrdn;
+extern BI_op_add ndb_back_add;
+extern BI_op_delete ndb_back_delete;
+
+extern BI_operational ndb_operational;
+extern BI_has_subordinates ndb_has_subordinates;
+extern BI_entry_get_rw ndb_entry_get;
+
+extern BI_tool_entry_open ndb_tool_entry_open;
+extern BI_tool_entry_close ndb_tool_entry_close;
+extern BI_tool_entry_first ndb_tool_entry_first;
+extern BI_tool_entry_next ndb_tool_entry_next;
+extern BI_tool_entry_get ndb_tool_entry_get;
+extern BI_tool_entry_put ndb_tool_entry_put;
+extern BI_tool_dn2id_get ndb_tool_dn2id_get;
+
+extern int ndb_modify_internal(
+ Operation *op,
+ NdbArgs *NA,
+ const char **text,
+ char *textbuf,
+ size_t textlen );
+
+extern int
+ndb_entry_get_data(
+ Operation *op,
+ NdbArgs *args,
+ int update );
+
+extern int
+ndb_entry_put_data(
+ BackendDB *be,
+ NdbArgs *args );
+
+extern int
+ndb_entry_del_data(
+ BackendDB *be,
+ NdbArgs *args );
+
+extern int
+ndb_entry_put_info(
+ BackendDB *be,
+ NdbArgs *args,
+ int update );
+
+extern int
+ndb_entry_get_info(
+ Operation *op,
+ NdbArgs *args,
+ int update,
+ struct berval *matched );
+
+extern "C" int
+ndb_entry_del_info(
+ BackendDB *be,
+ NdbArgs *args );
+
+extern int
+ndb_dn2rdns(
+ struct berval *dn,
+ NdbRdns *buf );
+
+extern NdbAttrInfo *
+ndb_ai_find( struct ndb_info *ni, AttributeType *at );
+
+extern NdbAttrInfo *
+ndb_ai_get( struct ndb_info *ni, struct berval *at );
+
+extern int
+ndb_aset_get( struct ndb_info *ni, struct berval *sname, struct berval *attrs, NdbOcInfo **ret );
+
+extern int
+ndb_aset_create( struct ndb_info *ni, NdbOcInfo *oci );
+
+extern int
+ndb_oc_read( struct ndb_info *ni, const NdbDictionary::Dictionary *dict );
+
+extern int
+ndb_oc_attrs(
+ NdbTransaction *txn,
+ const NdbDictionary::Table *myTable,
+ Entry *e,
+ NdbOcInfo *no,
+ NdbAttrInfo **attrs,
+ int nattrs,
+ Attribute *old );
+
+extern int
+ndb_has_children(
+ NdbArgs *NA,
+ int *hasChildren );
+
+extern struct berval *
+ndb_str2bvarray(
+ char *str,
+ int len,
+ char delim,
+ void *ctx );
+
+extern struct berval *
+ndb_ref2oclist(
+ const char *ref,
+ void *ctx );
+
+extern int
+ndb_next_id(
+ BackendDB *be,
+ Ndb *ndb,
+ ID *id );
+
+extern int
+ndb_thread_handle(
+ Operation *op,
+ Ndb **ndb );
+
+extern int
+ndb_back_init_cf(
+ BackendInfo *bi );
+
+extern "C" void
+ndb_trans_backoff( int num_retries );
+
+extern "C" void
+ndb_check_referral( Operation *op, SlapReply *rs, NdbArgs *NA );
+
+LDAP_END_DECL
+
+#endif /* _PROTO_NDB_H */
diff --git a/servers/slapd/back-ndb/search.cpp b/servers/slapd/back-ndb/search.cpp
new file mode 100644
index 0000000..3d0f4b0
--- /dev/null
+++ b/servers/slapd/back-ndb/search.cpp
@@ -0,0 +1,854 @@
+/* search.cpp - tools for slap tools */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+#include "lutil.h"
+
+#include "back-ndb.h"
+
+static int
+ndb_dn2bound(
+ NdbIndexScanOperation *myop,
+ NdbRdns *rdns
+)
+{
+ unsigned int i;
+
+ /* Walk thru RDNs */
+ for ( i=0; i<rdns->nr_num; i++ ) {
+ /* Note: RDN_COLUMN offset not needed here */
+ if ( myop->setBound( i, NdbIndexScanOperation::BoundEQ, rdns->nr_buf[i] ))
+ return LDAP_OTHER;
+ }
+ return i;
+}
+
+/* Check that all filter terms reside in the same table.
+ *
+ * If any of the filter terms are indexed, then only an IndexScan of the OL_index
+ * will be performed. If none are indexed, but all the terms reside in a single
+ * table, a Scan can be performed with the LDAP filter transformed into a ScanFilter.
+ *
+ * Otherwise, a full scan of the DB must be done with all filtering done by slapd.
+ */
+static int ndb_filter_check( struct ndb_info *ni, Filter *f,
+ NdbOcInfo **oci, int *indexed, int *ocfilter )
+{
+ AttributeDescription *ad = NULL;
+ ber_tag_t choice = f->f_choice;
+ int rc = 0, undef = 0;
+
+ if ( choice & SLAPD_FILTER_UNDEFINED ) {
+ choice &= SLAPD_FILTER_MASK;
+ undef = 1;
+ }
+ switch( choice ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ for ( f = f->f_list; f; f=f->f_next ) {
+ rc = ndb_filter_check( ni, f, oci, indexed, ocfilter );
+ if ( rc ) return rc;
+ }
+ break;
+ case LDAP_FILTER_PRESENT:
+ ad = f->f_desc;
+ break;
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_SUBSTRINGS:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ ad = f->f_av_desc;
+ break;
+ default:
+ break;
+ }
+ if ( ad && !undef ) {
+ NdbAttrInfo *ai;
+ /* ObjectClass filtering is in dn2id table */
+ if ( ad == slap_schema.si_ad_objectClass ) {
+ if ( choice == LDAP_FILTER_EQUALITY )
+ (*ocfilter)++;
+ return 0;
+ }
+ ai = ndb_ai_find( ni, ad->ad_type );
+ if ( ai ) {
+ if ( ai->na_flag & NDB_INFO_INDEX )
+ (*indexed)++;
+ if ( *oci ) {
+ if ( ai->na_oi != *oci )
+ rc = -1;
+ } else {
+ *oci = ai->na_oi;
+ }
+ }
+ }
+ return rc;
+}
+
+static int ndb_filter_set( Operation *op, struct ndb_info *ni, Filter *f, int indexed,
+ NdbIndexScanOperation *scan, NdbScanFilter *sf, int *bounds )
+{
+ AttributeDescription *ad = NULL;
+ ber_tag_t choice = f->f_choice;
+ int undef = 0;
+
+ if ( choice & SLAPD_FILTER_UNDEFINED ) {
+ choice &= SLAPD_FILTER_MASK;
+ undef = 1;
+ }
+ switch( choice ) {
+ case LDAP_FILTER_NOT:
+ /* no indexing for these */
+ break;
+ case LDAP_FILTER_OR:
+ /* FIXME: these bounds aren't right. */
+ if ( indexed ) {
+ scan->end_of_bound( (*bounds)++ );
+ }
+ case LDAP_FILTER_AND:
+ if ( sf ) {
+ sf->begin( choice == LDAP_FILTER_OR ? NdbScanFilter::OR : NdbScanFilter::AND );
+ }
+ for ( f = f->f_list; f; f=f->f_next ) {
+ if ( ndb_filter_set( op, ni, f, indexed, scan, sf, bounds ))
+ return -1;
+ }
+ if ( sf ) {
+ sf->end();
+ }
+ break;
+ case LDAP_FILTER_PRESENT:
+ ad = f->f_desc;
+ break;
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_SUBSTRINGS:
+ case LDAP_FILTER_GE:
+ case LDAP_FILTER_LE:
+ case LDAP_FILTER_APPROX:
+ ad = f->f_av_desc;
+ break;
+ default:
+ break;
+ }
+ if ( ad && !undef ) {
+ NdbAttrInfo *ai;
+ /* ObjectClass filtering is in dn2id table */
+ if ( ad == slap_schema.si_ad_objectClass ) {
+ return 0;
+ }
+ ai = ndb_ai_find( ni, ad->ad_type );
+ if ( ai ) {
+ int rc;
+ if ( ai->na_flag & NDB_INFO_INDEX ) {
+ char *buf, *ptr;
+ NdbIndexScanOperation::BoundType bt;
+
+ switch(choice) {
+ case LDAP_FILTER_PRESENT:
+ rc = scan->setBound( ai->na_ixcol - IDX_COLUMN,
+ NdbIndexScanOperation::BoundGT, NULL );
+ break;
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_APPROX:
+ bt = NdbIndexScanOperation::BoundEQ;
+ goto setit;
+ case LDAP_FILTER_GE:
+ bt = NdbIndexScanOperation::BoundGE;
+ goto setit;
+ case LDAP_FILTER_LE:
+ bt = NdbIndexScanOperation::BoundLE;
+ setit:
+ rc = f->f_av_value.bv_len+1;
+ if ( ai->na_len > 255 )
+ rc++;
+ buf = (char *)op->o_tmpalloc( rc, op->o_tmpmemctx );
+ rc = f->f_av_value.bv_len;
+ buf[0] = rc & 0xff;
+ ptr = buf+1;
+ if ( ai->na_len > 255 ) {
+ buf[1] = (rc >> 8);
+ ptr++;
+ }
+ memcpy( ptr, f->f_av_value.bv_val, f->f_av_value.bv_len );
+ rc = scan->setBound( ai->na_ixcol - IDX_COLUMN, bt, buf );
+ op->o_tmpfree( buf, op->o_tmpmemctx );
+ break;
+ default:
+ break;
+ }
+ } else if ( sf ) {
+ char *buf, *ptr;
+ NdbScanFilter::BinaryCondition bc;
+
+ switch(choice) {
+ case LDAP_FILTER_PRESENT:
+ rc = sf->isnotnull( ai->na_column );
+ break;
+ case LDAP_FILTER_EQUALITY:
+ case LDAP_FILTER_APPROX:
+ bc = NdbScanFilter::COND_EQ;
+ goto setf;
+ case LDAP_FILTER_GE:
+ bc = NdbScanFilter::COND_GE;
+ goto setf;
+ case LDAP_FILTER_LE:
+ bc = NdbScanFilter::COND_LE;
+ setf:
+ rc = sf->cmp( bc, ai->na_column, f->f_av_value.bv_val, f->f_av_value.bv_len );
+ break;
+ case LDAP_FILTER_SUBSTRINGS:
+ rc = 0;
+ if ( f->f_sub_initial.bv_val )
+ rc += f->f_sub_initial.bv_len + 1;
+ if ( f->f_sub_any ) {
+ int i;
+ if ( !rc ) rc++;
+ for (i=0; f->f_sub_any[i].bv_val; i++)
+ rc += f->f_sub_any[i].bv_len + 1;
+ }
+ if ( f->f_sub_final.bv_val ) {
+ if ( !rc ) rc++;
+ rc += f->f_sub_final.bv_len;
+ }
+ buf = (char *)op->o_tmpalloc( rc+1, op->o_tmpmemctx );
+ ptr = buf;
+ if ( f->f_sub_initial.bv_val ) {
+ memcpy( ptr, f->f_sub_initial.bv_val, f->f_sub_initial.bv_len );
+ ptr += f->f_sub_initial.bv_len;
+ *ptr++ = '%';
+ }
+ if ( f->f_sub_any ) {
+ int i;
+ if ( ptr == buf )
+ *ptr++ = '%';
+ for (i=0; f->f_sub_any[i].bv_val; i++) {
+ memcpy( ptr, f->f_sub_any[i].bv_val, f->f_sub_any[i].bv_len );
+ ptr += f->f_sub_any[i].bv_len;
+ *ptr++ = '%';
+ }
+ }
+ if ( f->f_sub_final.bv_val ) {
+ if ( ptr == buf )
+ *ptr++ = '%';
+ memcpy( ptr, f->f_sub_final.bv_val, f->f_sub_final.bv_len );
+ ptr += f->f_sub_final.bv_len;
+ }
+ *ptr = '\0';
+ rc = sf->cmp( NdbScanFilter::COND_LIKE, ai->na_column, buf, ptr - buf );
+ op->o_tmpfree( buf, op->o_tmpmemctx );
+ break;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int ndb_oc_search( Operation *op, SlapReply *rs, Ndb *ndb, NdbTransaction *txn,
+ NdbRdns *rbase, NdbOcInfo *oci, int indexed )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ const NdbDictionary::Dictionary *myDict = ndb->getDictionary();
+ const NdbDictionary::Table *myTable;
+ const NdbDictionary::Index *myIndex;
+ NdbIndexScanOperation *scan;
+ NdbIndexOperation *ixop;
+ NdbScanFilter *sf = NULL;
+ struct berval *ocs;
+ NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
+ char dnBuf[2048], *ptr;
+ NdbRdns rdns;
+ NdbArgs NA;
+ char idbuf[2*sizeof(ID)];
+ char ocbuf[NDB_OC_BUFLEN];
+ int i, rc, bounds;
+ Entry e = {0};
+ Uint64 eid;
+ time_t stoptime;
+ int manageDSAit;
+
+ stoptime = op->o_time + op->ors_tlimit;
+ manageDSAit = get_manageDSAit( op );
+
+ myTable = myDict->getTable( oci->no_table.bv_val );
+ if ( indexed ) {
+ scan = txn->getNdbIndexScanOperation( INDEX_NAME, DN2ID_TABLE );
+ if ( !scan )
+ return LDAP_OTHER;
+ scan->readTuples( NdbOperation::LM_CommittedRead );
+ } else {
+ myIndex = myDict->getIndex( "eid$unique", DN2ID_TABLE );
+ if ( !myIndex ) {
+ Debug( LDAP_DEBUG_ANY, DN2ID_TABLE " eid index is missing!\n", 0, 0, 0 );
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+ scan = (NdbIndexScanOperation *)txn->getNdbScanOperation( myTable );
+ if ( !scan )
+ return LDAP_OTHER;
+ scan->readTuples( NdbOperation::LM_CommittedRead );
+#if 1
+ sf = new NdbScanFilter(scan);
+ if ( !sf )
+ return LDAP_OTHER;
+ switch ( op->ors_filter->f_choice ) {
+ case LDAP_FILTER_AND:
+ case LDAP_FILTER_OR:
+ case LDAP_FILTER_NOT:
+ break;
+ default:
+ if ( sf->begin() < 0 ) {
+ rc = LDAP_OTHER;
+ goto leave;
+ }
+ }
+#endif
+ }
+
+ bounds = 0;
+ rc = ndb_filter_set( op, ni, op->ors_filter, indexed, scan, sf, &bounds );
+ if ( rc )
+ goto leave;
+ if ( sf ) sf->end();
+
+ scanID = scan->getValue( EID_COLUMN, idbuf );
+ if ( indexed ) {
+ scanOC = scan->getValue( OCS_COLUMN, ocbuf );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ rdns.nr_buf[i][0] = '\0';
+ scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
+ }
+ }
+
+ if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+
+ e.e_name.bv_val = dnBuf;
+ NA.e = &e;
+ NA.ndb = ndb;
+ while ( scan->nextResult( true, true ) == 0 ) {
+ NdbTransaction *tx2;
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ break;
+ }
+ if ( slapd_shutdown ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ break;
+ }
+ if ( op->ors_tlimit != SLAP_NO_LIMIT &&
+ slap_get_time() > stoptime ) {
+ rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+ break;
+ }
+
+ eid = scanID->u_64_value();
+ e.e_id = eid;
+ if ( !indexed ) {
+ tx2 = ndb->startTransaction( myTable );
+ if ( !tx2 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+
+ ixop = tx2->getNdbIndexOperation( myIndex );
+ if ( !ixop ) {
+ tx2->close();
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+ ixop->readTuple( NdbOperation::LM_CommittedRead );
+ ixop->equal( EID_COLUMN, eid );
+
+ scanOC = ixop->getValue( OCS_COLUMN, ocbuf );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ rdns.nr_buf[i][0] = '\0';
+ scanDN[i] = ixop->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
+ }
+ rc = tx2->execute( NdbTransaction::Commit, NdbOperation::AbortOnError, 1 );
+ tx2->close();
+ if ( rc ) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+ }
+
+ ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
+ break;
+ }
+ rdns.nr_num = i;
+
+ /* entry must be subordinate to the base */
+ if ( i < rbase->nr_num ) {
+ continue;
+ }
+
+ ptr = dnBuf;
+ for ( --i; i>=0; i-- ) {
+ char *buf;
+ int len;
+ buf = rdns.nr_buf[i];
+ len = *buf++;
+ ptr = lutil_strncopy( ptr, buf, len );
+ if ( i ) *ptr++ = ',';
+ }
+ *ptr = '\0';
+ e.e_name.bv_len = ptr - dnBuf;
+
+ /* More scope checks */
+ /* If indexed, these can be moved into the ScanFilter */
+ switch( op->ors_scope ) {
+ case LDAP_SCOPE_ONELEVEL:
+ if ( rdns.nr_num != rbase->nr_num+1 )
+ continue;
+ case LDAP_SCOPE_SUBORDINATE:
+ if ( rdns.nr_num == rbase->nr_num )
+ continue;
+ case LDAP_SCOPE_SUBTREE:
+ default:
+ if ( e.e_name.bv_len <= op->o_req_dn.bv_len ) {
+ if ( op->ors_scope != LDAP_SCOPE_SUBTREE ||
+ strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val ))
+ continue;
+ } else if ( strcasecmp( op->o_req_dn.bv_val, e.e_name.bv_val +
+ e.e_name.bv_len - op->o_req_dn.bv_len ))
+ continue;
+ }
+
+ dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
+ {
+#if 1 /* NDBapi was broken here but seems to work now */
+ Ndb::Key_part_ptr keys[2];
+ char xbuf[512];
+ keys[0].ptr = &eid;
+ keys[0].len = sizeof(eid);
+ keys[1].ptr = NULL;
+ keys[1].len = 0;
+ tx2 = ndb->startTransaction( myTable, keys, xbuf, sizeof(xbuf));
+#else
+ tx2 = ndb->startTransaction( myTable );
+#endif
+ if ( !tx2 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+ NA.txn = tx2;
+ NA.ocs = ocs;
+ rc = ndb_entry_get_data( op, &NA, 0 );
+ tx2->close();
+ }
+ ber_bvarray_free_x( ocs, op->o_tmpmemctx );
+ if ( !manageDSAit && is_entry_referral( &e )) {
+ BerVarray erefs = get_entry_referrals( op, &e );
+ rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
+ op->ors_scope == LDAP_SCOPE_ONELEVEL ?
+ LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
+ rc = send_search_reference( op, rs );
+ ber_bvarray_free( rs->sr_ref );
+ ber_bvarray_free( erefs );
+ rs->sr_ref = NULL;
+ } else if ( manageDSAit || !is_entry_glue( &e )) {
+ rc = test_filter( op, &e, op->ors_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_entry = &e;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_flags = 0;
+ rc = send_search_entry( op, rs );
+ rs->sr_entry = NULL;
+ rs->sr_attrs = NULL;
+ } else {
+ rc = 0;
+ }
+ }
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
+ if ( rc ) break;
+ }
+leave:
+ if ( sf ) delete sf;
+ return rc;
+}
+
+extern "C"
+int ndb_back_search( Operation *op, SlapReply *rs )
+{
+ struct ndb_info *ni = (struct ndb_info *) op->o_bd->be_private;
+ NdbTransaction *txn;
+ NdbIndexScanOperation *scan;
+ NdbScanFilter *sf = NULL;
+ Entry e = {0};
+ int rc, i, ocfilter, indexed;
+ struct berval matched;
+ NdbRecAttr *scanID, *scanOC, *scanDN[NDB_MAX_RDNS];
+ char dnBuf[2048], *ptr;
+ char idbuf[2*sizeof(ID)];
+ char ocbuf[NDB_OC_BUFLEN];
+ NdbRdns rdns;
+ NdbOcInfo *oci;
+ NdbArgs NA;
+ slap_mask_t mask;
+ time_t stoptime;
+ int manageDSAit;
+
+ rc = ndb_thread_handle( op, &NA.ndb );
+ rdns.nr_num = 0;
+
+ manageDSAit = get_manageDSAit( op );
+
+ txn = NA.ndb->startTransaction();
+ if ( !txn ) {
+ Debug( LDAP_DEBUG_TRACE,
+ LDAP_XSTRING(ndb_back_search) ": startTransaction failed: %s (%d)\n",
+ NA.ndb->getNdbError().message, NA.ndb->getNdbError().code, 0 );
+ rs->sr_err = LDAP_OTHER;
+ rs->sr_text = "internal error";
+ goto leave;
+ }
+
+ NA.txn = txn;
+ e.e_name = op->o_req_dn;
+ e.e_nname = op->o_req_ndn;
+ NA.e = &e;
+ NA.rdns = &rdns;
+ NA.ocs = NULL;
+
+ rs->sr_err = ndb_entry_get_info( op, &NA, 0, &matched );
+ if ( rs->sr_err ) {
+ if ( rs->sr_err == LDAP_NO_SUCH_OBJECT ) {
+ rs->sr_matched = matched.bv_val;
+ if ( NA.ocs )
+ ndb_check_referral( op, rs, &NA );
+ }
+ goto leave;
+ }
+
+ if ( !access_allowed_mask( op, &e, slap_schema.si_ad_entry,
+ NULL, ACL_SEARCH, NULL, &mask )) {
+ if ( !ACL_GRANT( mask, ACL_DISCLOSE ))
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ else
+ rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ goto leave;
+ }
+
+ rs->sr_err = ndb_entry_get_data( op, &NA, 0 );
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ if ( rs->sr_err )
+ goto leave;
+
+ if ( !manageDSAit && is_entry_referral( &e )) {
+ rs->sr_ref = get_entry_referrals( op, &e );
+ rs->sr_err = LDAP_REFERRAL;
+ if ( rs->sr_ref )
+ rs->sr_flags |= REP_REF_MUSTBEFREED;
+ rs->sr_matched = e.e_name.bv_val;
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ goto leave;
+ }
+
+ if ( !manageDSAit && is_entry_glue( &e )) {
+ rs->sr_err = LDAP_NO_SUCH_OBJECT;
+ goto leave;
+ }
+
+ if ( get_assert( op ) && test_filter( op, &e, (Filter *)get_assertion( op )) !=
+ LDAP_COMPARE_TRUE ) {
+ rs->sr_err = LDAP_ASSERTION_FAILED;
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ goto leave;
+ }
+
+ /* admin ignores tlimits */
+ stoptime = op->o_time + op->ors_tlimit;
+
+ if ( op->ors_scope == LDAP_SCOPE_BASE ) {
+ rc = test_filter( op, &e, op->ors_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_entry = &e;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_flags = 0;
+ send_search_entry( op, rs );
+ rs->sr_entry = NULL;
+ }
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ rs->sr_err = LDAP_SUCCESS;
+ goto leave;
+ } else {
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ if ( rdns.nr_num == NDB_MAX_RDNS ) {
+ if ( op->ors_scope == LDAP_SCOPE_ONELEVEL ||
+ op->ors_scope == LDAP_SCOPE_CHILDREN )
+ rs->sr_err = LDAP_SUCCESS;
+ goto leave;
+ }
+ }
+
+ /* See if we can handle the filter. Filtering on objectClass is only done
+ * in the DN2ID table scan. If all other filter terms reside in one table,
+ * then we scan the OC table instead of the DN2ID table.
+ */
+ oci = NULL;
+ indexed = 0;
+ ocfilter = 0;
+ rc = ndb_filter_check( ni, op->ors_filter, &oci, &indexed, &ocfilter );
+ if ( rc ) {
+ Debug( LDAP_DEBUG_TRACE, "ndb_back_search: "
+ "filter attributes from multiple tables, indexing ignored\n",
+ 0, 0, 0 );
+ } else if ( oci ) {
+ rc = ndb_oc_search( op, rs, NA.ndb, txn, &rdns, oci, indexed );
+ goto leave;
+ }
+
+ scan = txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
+ if ( !scan ) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+ scan->readTuples( NdbOperation::LM_CommittedRead );
+ rc = ndb_dn2bound( scan, &rdns );
+
+ /* TODO: if ( ocfilter ) set up scanfilter for objectclass matches
+ * column COND_LIKE "% <class> %"
+ */
+
+ switch( op->ors_scope ) {
+ case LDAP_SCOPE_ONELEVEL:
+ sf = new NdbScanFilter(scan);
+ if ( sf->begin() < 0 ||
+ sf->cmp(NdbScanFilter::COND_NOT_LIKE, rc+3, "_%",
+ STRLENOF("_%")) < 0 ||
+ sf->end() < 0 ) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+ /* FALLTHRU */
+ case LDAP_SCOPE_CHILDREN:
+ /* Note: RDN_COLUMN offset not needed here */
+ scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
+ /* FALLTHRU */
+ case LDAP_SCOPE_SUBTREE:
+ break;
+ }
+ scanID = scan->getValue( EID_COLUMN, idbuf );
+ scanOC = scan->getValue( OCS_COLUMN, ocbuf );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ rdns.nr_buf[i][0] = '\0';
+ scanDN[i] = scan->getValue( RDN_COLUMN+i, rdns.nr_buf[i] );
+ }
+ if ( txn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 )) {
+ rs->sr_err = LDAP_OTHER;
+ goto leave;
+ }
+
+ e.e_name.bv_val = dnBuf;
+ while ( scan->nextResult( true, true ) == 0 ) {
+ if ( op->o_abandon ) {
+ rs->sr_err = SLAPD_ABANDON;
+ break;
+ }
+ if ( slapd_shutdown ) {
+ rs->sr_err = LDAP_UNAVAILABLE;
+ break;
+ }
+ if ( op->ors_tlimit != SLAP_NO_LIMIT &&
+ slap_get_time() > stoptime ) {
+ rs->sr_err = LDAP_TIMELIMIT_EXCEEDED;
+ break;
+ }
+ e.e_id = scanID->u_64_value();
+ NA.ocs = ndb_ref2oclist( ocbuf, op->o_tmpmemctx );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ if ( scanDN[i]->isNULL() || !rdns.nr_buf[i][0] )
+ break;
+ }
+ ptr = dnBuf;
+ rdns.nr_num = i;
+ for ( --i; i>=0; i-- ) {
+ char *buf;
+ int len;
+ buf = rdns.nr_buf[i];
+ len = *buf++;
+ ptr = lutil_strncopy( ptr, buf, len );
+ if ( i ) *ptr++ = ',';
+ }
+ *ptr = '\0';
+ e.e_name.bv_len = ptr - dnBuf;
+ dnNormalize( 0, NULL, NULL, &e.e_name, &e.e_nname, op->o_tmpmemctx );
+ NA.txn = NA.ndb->startTransaction();
+ rc = ndb_entry_get_data( op, &NA, 0 );
+ NA.txn->close();
+ ber_bvarray_free_x( NA.ocs, op->o_tmpmemctx );
+ if ( !manageDSAit && is_entry_referral( &e )) {
+ BerVarray erefs = get_entry_referrals( op, &e );
+ rs->sr_ref = referral_rewrite( erefs, &e.e_name, NULL,
+ op->ors_scope == LDAP_SCOPE_ONELEVEL ?
+ LDAP_SCOPE_BASE : LDAP_SCOPE_SUBTREE );
+ rc = send_search_reference( op, rs );
+ ber_bvarray_free( rs->sr_ref );
+ ber_bvarray_free( erefs );
+ rs->sr_ref = NULL;
+ } else if ( manageDSAit || !is_entry_glue( &e )) {
+ rc = test_filter( op, &e, op->ors_filter );
+ if ( rc == LDAP_COMPARE_TRUE ) {
+ rs->sr_entry = &e;
+ rs->sr_attrs = op->ors_attrs;
+ rs->sr_flags = 0;
+ rc = send_search_entry( op, rs );
+ rs->sr_entry = NULL;
+ } else {
+ rc = 0;
+ }
+ }
+ attrs_free( e.e_attrs );
+ e.e_attrs = NULL;
+ op->o_tmpfree( e.e_nname.bv_val, op->o_tmpmemctx );
+ if ( rc ) break;
+ }
+leave:
+ if ( sf )
+ delete sf;
+ if ( txn )
+ txn->close();
+ send_ldap_result( op, rs );
+ return rs->sr_err;
+}
+
+extern NdbInterpretedCode *ndb_lastrow_code; /* init.cpp */
+
+extern "C" int
+ndb_has_children(
+ NdbArgs *NA,
+ int *hasChildren
+)
+{
+ NdbIndexScanOperation *scan;
+ char idbuf[2*sizeof(ID)];
+ int rc;
+
+ if ( NA->rdns->nr_num >= NDB_MAX_RDNS ) {
+ *hasChildren = LDAP_COMPARE_FALSE;
+ return 0;
+ }
+
+ scan = NA->txn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
+ if ( !scan )
+ return LDAP_OTHER;
+ scan->readTuples( NdbOperation::LM_Read, 0U, 0U, 1U );
+ rc = ndb_dn2bound( scan, NA->rdns );
+ if ( rc < NDB_MAX_RDNS ) {
+ scan->setBound( rc, NdbIndexScanOperation::BoundLT, "\0" );
+ }
+#if 0
+ scan->interpret_exit_last_row();
+#else
+ scan->setInterpretedCode(ndb_lastrow_code);
+#endif
+ scan->getValue( EID_COLUMN, idbuf );
+ if ( NA->txn->execute( NdbTransaction::NoCommit, NdbOperation::AO_IgnoreError, 1 )) {
+ return LDAP_OTHER;
+ }
+ if (rc < NDB_MAX_RDNS && scan->nextResult( true, true ) == 0 )
+ *hasChildren = LDAP_COMPARE_TRUE;
+ else
+ *hasChildren = LDAP_COMPARE_FALSE;
+ scan->close();
+ return 0;
+}
+
+extern "C" int
+ndb_has_subordinates(
+ Operation *op,
+ Entry *e,
+ int *hasSubordinates )
+{
+ NdbArgs NA;
+ NdbRdns rdns;
+ int rc;
+
+ NA.rdns = &rdns;
+ rc = ndb_dn2rdns( &e->e_nname, &rdns );
+
+ if ( rc == 0 ) {
+ rc = ndb_thread_handle( op, &NA.ndb );
+ NA.txn = NA.ndb->startTransaction();
+ if ( NA.txn ) {
+ rc = ndb_has_children( &NA, hasSubordinates );
+ NA.txn->close();
+ }
+ }
+
+ return rc;
+}
+
+/*
+ * sets the supported operational attributes (if required)
+ */
+extern "C" int
+ndb_operational(
+ Operation *op,
+ SlapReply *rs )
+{
+ Attribute **ap;
+
+ assert( rs->sr_entry != NULL );
+
+ for ( ap = &rs->sr_operational_attrs; *ap; ap = &(*ap)->a_next ) {
+ 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 = ndb_has_subordinates( op, rs->sr_entry, &hasSubordinates );
+ if ( rc == LDAP_SUCCESS ) {
+ *ap = slap_operational_hasSubordinate( hasSubordinates == LDAP_COMPARE_TRUE );
+ assert( *ap != NULL );
+
+ ap = &(*ap)->a_next;
+ }
+ }
+
+ return LDAP_SUCCESS;
+}
+
diff --git a/servers/slapd/back-ndb/tools.cpp b/servers/slapd/back-ndb/tools.cpp
new file mode 100644
index 0000000..13ea7c3
--- /dev/null
+++ b/servers/slapd/back-ndb/tools.cpp
@@ -0,0 +1,544 @@
+/* tools.cpp - tools for slap tools */
+/* $OpenLDAP$ */
+/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
+ *
+ * Copyright 2008-2022 The OpenLDAP Foundation.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted only as authorized by the OpenLDAP
+ * Public License.
+ *
+ * A copy of this license is available in the file LICENSE in the
+ * top-level directory of the distribution or, alternatively, at
+ * <http://www.OpenLDAP.org/license.html>.
+ */
+/* ACKNOWLEDGEMENTS:
+ * This work was initially developed by Howard Chu for inclusion
+ * in OpenLDAP Software. This work was sponsored by MySQL.
+ */
+
+#include "portable.h"
+
+#include <stdio.h>
+#include <ac/string.h>
+#include <ac/errno.h>
+
+#include "lutil.h"
+
+#include "back-ndb.h"
+
+typedef struct dn_id {
+ ID id;
+ struct berval dn;
+} dn_id;
+
+#define HOLE_SIZE 4096
+static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
+static unsigned nhmax = HOLE_SIZE;
+static unsigned nholes;
+static Avlnode *myParents;
+
+static Ndb *myNdb;
+static NdbTransaction *myScanTxn;
+static NdbIndexScanOperation *myScanOp;
+
+static NdbRecAttr *myScanID, *myScanOC;
+static NdbRecAttr *myScanDN[NDB_MAX_RDNS];
+static char myDNbuf[2048];
+static char myIdbuf[2*sizeof(ID)];
+static char myOcbuf[NDB_OC_BUFLEN];
+static NdbRdns myRdns;
+
+static NdbTransaction *myPutTxn;
+static int myPutCnt;
+
+static struct berval *myOcList;
+static struct berval myDn;
+
+extern "C"
+int ndb_tool_entry_open(
+ BackendDB *be, int mode )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+
+ myNdb = new Ndb( ni->ni_cluster[0], ni->ni_dbname );
+ return myNdb->init(1024);
+}
+
+extern "C"
+int ndb_tool_entry_close(
+ BackendDB *be )
+{
+ if ( myPutTxn ) {
+ int rc = myPutTxn->execute(NdbTransaction::Commit);
+ if( rc != 0 ) {
+ char text[1024];
+ snprintf( text, sizeof(text),
+ "txn_commit failed: %s (%d)",
+ myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+ text, 0, 0 );
+ }
+ myPutTxn->close();
+ myPutTxn = NULL;
+ }
+ myPutCnt = 0;
+
+ if( nholes ) {
+ unsigned i;
+ fprintf( stderr, "Error, entries missing!\n");
+ for (i=0; i<nholes; i++) {
+ fprintf(stderr, " entry %ld: %s\n",
+ holes[i].id, holes[i].dn.bv_val);
+ }
+ return -1;
+ }
+
+ return 0;
+}
+
+extern "C"
+ID ndb_tool_entry_next(
+ BackendDB *be )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ char *ptr;
+ ID id;
+ int i;
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ if ( myScanOp->nextResult() ) {
+ myScanOp->close();
+ myScanOp = NULL;
+ myScanTxn->close();
+ myScanTxn = NULL;
+ return NOID;
+ }
+ id = myScanID->u_64_value();
+
+ if ( myOcList ) {
+ ber_bvarray_free( myOcList );
+ }
+ myOcList = ndb_ref2oclist( myOcbuf, NULL );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ if ( myScanDN[i]->isNULL() || !myRdns.nr_buf[i][0] )
+ break;
+ }
+ myRdns.nr_num = i;
+ ptr = myDNbuf;
+ for ( --i; i>=0; i-- ) {
+ char *buf;
+ int len;
+ buf = myRdns.nr_buf[i];
+ len = *buf++;
+ ptr = lutil_strncopy( ptr, buf, len );
+ if ( i )
+ *ptr++ = ',';
+ }
+ *ptr = '\0';
+ myDn.bv_val = myDNbuf;
+ myDn.bv_len = ptr - myDNbuf;
+
+ return id;
+}
+
+extern "C"
+ID ndb_tool_entry_first(
+ BackendDB *be )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ int i;
+
+ myScanTxn = myNdb->startTransaction();
+ if ( !myScanTxn )
+ return NOID;
+
+ myScanOp = myScanTxn->getNdbIndexScanOperation( "PRIMARY", DN2ID_TABLE );
+ if ( !myScanOp )
+ return NOID;
+
+ if ( myScanOp->readTuples( NdbOperation::LM_CommittedRead, NdbScanOperation::SF_KeyInfo ))
+ return NOID;
+
+ myScanID = myScanOp->getValue( EID_COLUMN, myIdbuf );
+ myScanOC = myScanOp->getValue( OCS_COLUMN, myOcbuf );
+ for ( i=0; i<NDB_MAX_RDNS; i++ ) {
+ myScanDN[i] = myScanOp->getValue( i+RDN_COLUMN, myRdns.nr_buf[i] );
+ }
+ if ( myScanTxn->execute( NdbTransaction::NoCommit, NdbOperation::AbortOnError, 1 ))
+ return NOID;
+
+ return ndb_tool_entry_next( be );
+}
+
+extern "C"
+ID ndb_tool_dn2id_get(
+ Backend *be,
+ struct berval *dn
+)
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ NdbArgs NA;
+ NdbRdns rdns;
+ Entry e;
+ char text[1024];
+ Operation op = {0};
+ Opheader ohdr = {0};
+ int rc;
+
+ if ( BER_BVISEMPTY(dn) )
+ return 0;
+
+ NA.ndb = myNdb;
+ NA.txn = myNdb->startTransaction();
+ if ( !NA.txn ) {
+ snprintf( text, sizeof(text),
+ "startTransaction failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_dn2id_get) ": %s\n",
+ text, 0, 0 );
+ return NOID;
+ }
+ if ( myOcList ) {
+ ber_bvarray_free( myOcList );
+ myOcList = NULL;
+ }
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ NA.e = &e;
+ e.e_name = *dn;
+ NA.rdns = &rdns;
+ NA.ocs = NULL;
+ rc = ndb_entry_get_info( &op, &NA, 0, NULL );
+ myOcList = NA.ocs;
+ NA.txn->close();
+ if ( rc )
+ return NOID;
+
+ myDn = *dn;
+
+ return e.e_id;
+}
+
+extern "C"
+Entry* ndb_tool_entry_get( BackendDB *be, ID id )
+{
+ NdbArgs NA;
+ int rc;
+ char text[1024];
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ NA.txn = myNdb->startTransaction();
+ if ( !NA.txn ) {
+ snprintf( text, sizeof(text),
+ "start_transaction failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_entry_get) ": %s\n",
+ text, 0, 0 );
+ return NULL;
+ }
+
+ NA.e = entry_alloc();
+ NA.e->e_id = id;
+ ber_dupbv( &NA.e->e_name, &myDn );
+ dnNormalize( 0, NULL, NULL, &NA.e->e_name, &NA.e->e_nname, NULL );
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ NA.ndb = myNdb;
+ NA.ocs = myOcList;
+ rc = ndb_entry_get_data( &op, &NA, 0 );
+
+ if ( rc ) {
+ entry_free( NA.e );
+ NA.e = NULL;
+ }
+ NA.txn->close();
+
+ return NA.e;
+}
+
+static struct berval glueval[] = {
+ BER_BVC("glue"),
+ BER_BVNULL
+};
+
+static int ndb_dnid_cmp( const void *v1, const void *v2 )
+{
+ struct dn_id *dn1 = (struct dn_id *)v1,
+ *dn2 = (struct dn_id *)v2;
+ return ber_bvcmp( &dn1->dn, &dn2->dn );
+}
+
+static int ndb_tool_next_id(
+ Operation *op,
+ NdbArgs *NA,
+ struct berval *text,
+ int hole )
+{
+ struct berval ndn = NA->e->e_nname;
+ int rc;
+
+ if (ndn.bv_len == 0) {
+ NA->e->e_id = 0;
+ return 0;
+ }
+
+ rc = ndb_entry_get_info( op, NA, 0, NULL );
+ if ( rc ) {
+ Attribute *a, tmp = {0};
+ if ( !be_issuffix( op->o_bd, &ndn ) ) {
+ struct dn_id *dptr;
+ struct berval npdn;
+ dnParent( &ndn, &npdn );
+ NA->e->e_nname = npdn;
+ NA->rdns->nr_num--;
+ rc = ndb_tool_next_id( op, NA, text, 1 );
+ NA->e->e_nname = ndn;
+ NA->rdns->nr_num++;
+ if ( rc ) {
+ return rc;
+ }
+ /* If parent didn't exist, it was created just now
+ * and its ID is now in e->e_id.
+ */
+ dptr = (struct dn_id *)ch_malloc( sizeof( struct dn_id ) + npdn.bv_len + 1);
+ dptr->id = NA->e->e_id;
+ dptr->dn.bv_val = (char *)(dptr+1);
+ strcpy(dptr->dn.bv_val, npdn.bv_val );
+ dptr->dn.bv_len = npdn.bv_len;
+ if ( avl_insert( &myParents, dptr, ndb_dnid_cmp, avl_dup_error )) {
+ ch_free( dptr );
+ }
+ }
+ rc = ndb_next_id( op->o_bd, myNdb, &NA->e->e_id );
+ if ( rc ) {
+ snprintf( text->bv_val, text->bv_len,
+ "next_id failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
+ return rc;
+ }
+ if ( hole ) {
+ a = NA->e->e_attrs;
+ NA->e->e_attrs = &tmp;
+ tmp.a_desc = slap_schema.si_ad_objectClass;
+ tmp.a_vals = glueval;
+ tmp.a_nvals = tmp.a_vals;
+ tmp.a_numvals = 1;
+ }
+ rc = ndb_entry_put_info( op->o_bd, NA, 0 );
+ if ( hole ) {
+ NA->e->e_attrs = a;
+ }
+ if ( rc ) {
+ snprintf( text->bv_val, text->bv_len,
+ "ndb_entry_put_info failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
+ } else if ( hole ) {
+ if ( nholes == nhmax - 1 ) {
+ if ( holes == hbuf ) {
+ holes = (dn_id *)ch_malloc( nhmax * sizeof(dn_id) * 2 );
+ AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
+ } else {
+ holes = (dn_id *)ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
+ }
+ nhmax *= 2;
+ }
+ ber_dupbv( &holes[nholes].dn, &ndn );
+ holes[nholes++].id = NA->e->e_id;
+ }
+ } else if ( !hole ) {
+ unsigned i;
+
+ for ( i=0; i<nholes; i++) {
+ if ( holes[i].id == NA->e->e_id ) {
+ int j;
+ free(holes[i].dn.bv_val);
+ for (j=i;j<nholes;j++) holes[j] = holes[j+1];
+ holes[j].id = 0;
+ nholes--;
+ rc = ndb_entry_put_info( op->o_bd, NA, 1 );
+ break;
+ } else if ( holes[i].id > NA->e->e_id ) {
+ break;
+ }
+ }
+ }
+ return rc;
+}
+
+extern "C"
+ID ndb_tool_entry_put(
+ BackendDB *be,
+ Entry *e,
+ struct berval *text )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ struct dn_id dtmp, *dptr;
+ NdbArgs NA;
+ NdbRdns rdns;
+ int rc, slow = 0;
+ Operation op = {0};
+ Opheader ohdr = {0};
+
+ assert( be != NULL );
+ assert( slapMode & SLAP_TOOL_MODE );
+
+ assert( text != NULL );
+ assert( text->bv_val != NULL );
+ assert( text->bv_val[0] == '\0' ); /* overconservative? */
+
+ Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(ndb_tool_entry_put)
+ "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
+
+ if ( !be_issuffix( be, &e->e_nname )) {
+ dnParent( &e->e_nname, &dtmp.dn );
+ dptr = (struct dn_id *)avl_find( myParents, &dtmp, ndb_dnid_cmp );
+ if ( !dptr )
+ slow = 1;
+ }
+
+ rdns.nr_num = 0;
+
+ op.o_hdr = &ohdr;
+ op.o_bd = be;
+ op.o_tmpmemctx = NULL;
+ op.o_tmpmfuncs = &ch_mfuncs;
+
+ if ( !slow ) {
+ rc = ndb_next_id( be, myNdb, &e->e_id );
+ if ( rc ) {
+ snprintf( text->bv_val, text->bv_len,
+ "next_id failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> ndb_tool_next_id: %s\n", text->bv_val, 0, 0 );
+ return rc;
+ }
+ }
+
+ if ( !myPutTxn )
+ myPutTxn = myNdb->startTransaction();
+ if ( !myPutTxn ) {
+ snprintf( text->bv_val, text->bv_len,
+ "start_transaction failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+ text->bv_val, 0, 0 );
+ return NOID;
+ }
+
+ /* add dn2id indices */
+ ndb_dn2rdns( &e->e_name, &rdns );
+ NA.rdns = &rdns;
+ NA.e = e;
+ NA.ndb = myNdb;
+ NA.txn = myPutTxn;
+ if ( slow ) {
+ rc = ndb_tool_next_id( &op, &NA, text, 0 );
+ if( rc != 0 ) {
+ goto done;
+ }
+ } else {
+ rc = ndb_entry_put_info( be, &NA, 0 );
+ if ( rc != 0 ) {
+ goto done;
+ }
+ }
+
+ /* id2entry index */
+ rc = ndb_entry_put_data( be, &NA );
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "ndb_entry_put_data failed: %s (%d)",
+ myNdb->getNdbError().message, myNdb->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+ text->bv_val, 0, 0 );
+ goto done;
+ }
+
+done:
+ if( rc == 0 ) {
+ myPutCnt++;
+ if ( !( myPutCnt & 0x0f )) {
+ rc = myPutTxn->execute(NdbTransaction::Commit);
+ if( rc != 0 ) {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_commit failed: %s (%d)",
+ myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+ text->bv_val, 0, 0 );
+ e->e_id = NOID;
+ }
+ myPutTxn->close();
+ myPutTxn = NULL;
+ }
+ } else {
+ snprintf( text->bv_val, text->bv_len,
+ "txn_aborted! %s (%d)",
+ myPutTxn->getNdbError().message, myPutTxn->getNdbError().code );
+ Debug( LDAP_DEBUG_ANY,
+ "=> " LDAP_XSTRING(ndb_tool_entry_put) ": %s\n",
+ text->bv_val, 0, 0 );
+ e->e_id = NOID;
+ myPutTxn->close();
+ }
+
+ return e->e_id;
+}
+
+extern "C"
+int ndb_tool_entry_reindex(
+ BackendDB *be,
+ ID id,
+ AttributeDescription **adv )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+
+ Debug( LDAP_DEBUG_ARGS,
+ "=> " LDAP_XSTRING(ndb_tool_entry_reindex) "( %ld )\n",
+ (long) id, 0, 0 );
+
+ return 0;
+}
+
+extern "C"
+ID ndb_tool_entry_modify(
+ BackendDB *be,
+ Entry *e,
+ struct berval *text )
+{
+ struct ndb_info *ni = (struct ndb_info *) be->be_private;
+ int rc;
+
+ Debug( LDAP_DEBUG_TRACE,
+ "=> " LDAP_XSTRING(ndb_tool_entry_modify) "( %ld, \"%s\" )\n",
+ (long) e->e_id, e->e_dn, 0 );
+
+done:
+ return e->e_id;
+}
+