]> git.sur5r.net Git - openldap/blobdiff - servers/slapd/back-mdb/modify.c
back-mdb Multival fixes
[openldap] / servers / slapd / back-mdb / modify.c
index 35682cefe8845fc70feb065fac23c440b95be3c5..e0ca295904de856d85c1de72bd7a7f5021fd7c13 100644 (file)
@@ -2,7 +2,7 @@
 /* $OpenLDAP$ */
 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
  *
- * Copyright 2000-2015 The OpenLDAP Foundation.
+ * Copyright 2000-2017 The OpenLDAP Foundation.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -49,11 +49,20 @@ mdb_modify_idxflags(
                        ap = attr_find( oldattrs, desc );
                        if ( ap ) ap->a_flags |= SLAP_ATTR_IXDEL;
 
-                       /* Find all other attrs that index to same slot */
-                       for ( ap = newattrs; ap; ap = ap->a_next ) {
-                               ai = mdb_index_mask( op->o_bd, ap->a_desc, &ix2 );
-                               if ( ai && ix2.bv_val == ix_at.bv_val )
-                                       ap->a_flags |= SLAP_ATTR_IXADD;
+                       /* ITS#8678 FIXME
+                        * If using 32bit hashes, or substring index, must account for
+                        * possible index collisions. If no substring index, and using
+                        * 64bit hashes, assume we don't need to check for collisions.
+                        *
+                        * In 2.5 use refcounts and avoid all of this mess.
+                        */
+                       if (!slap_hash64(-1) || (ai->ai_indexmask & SLAP_INDEX_SUBSTR)) {
+                               /* Find all other attrs that index to same slot */
+                               for ( ap = newattrs; ap; ap = ap->a_next ) {
+                                       ai = mdb_index_mask( op->o_bd, ap->a_desc, &ix2 );
+                                       if ( ai && ix2.bv_val == ix_at.bv_val )
+                                               ap->a_flags |= SLAP_ATTR_IXADD;
+                               }
                        }
 
                } else {
@@ -74,13 +83,17 @@ int mdb_modify_internal(
        char *textbuf,
        size_t textlen )
 {
+       struct mdb_info *mdb = (struct mdb_info *) op->o_bd->be_private;
        int rc, err;
        Modification    *mod;
        Modifications   *ml;
        Attribute       *save_attrs;
-       Attribute       *ap;
+       Attribute       *ap, *aold, *anew;
        int                     glue_attr_delete = 0;
+       int                     softop, chkpresent;
        int                     got_delete;
+       int                     a_flags;
+       MDB_cursor      *mvc = NULL;
 
        Debug( LDAP_DEBUG_TRACE, "mdb_modify_internal: 0x%08lx: %s\n",
                e->e_id, e->e_dn, 0);
@@ -129,16 +142,69 @@ int mdb_modify_internal(
                mod = &ml->sml_mod;
                got_delete = 0;
 
+               aold = attr_find( e->e_attrs, mod->sm_desc );
+               if (aold)
+                       a_flags = aold->a_flags;
+               else
+                       a_flags = 0;
+
                switch ( mod->sm_op ) {
                case LDAP_MOD_ADD:
+                       softop = 0;
+                       chkpresent = 0;
                        Debug(LDAP_DEBUG_ARGS,
                                "mdb_modify_internal: add %s\n",
                                mod->sm_desc->ad_cname.bv_val, 0, 0);
+
+do_add:
                        err = modify_add_values( e, mod, get_permissiveModify(op),
                                text, textbuf, textlen );
+
+                       if( softop ) {
+                               mod->sm_op = SLAP_MOD_SOFTADD;
+                               if ( err == LDAP_TYPE_OR_VALUE_EXISTS )
+                                       err = LDAP_SUCCESS;
+                       }
+                       if( chkpresent ) {
+                               mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
+                       }
+
                        if( err != LDAP_SUCCESS ) {
                                Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
                                        err, *text, 0);
+                       } else {
+                               if (!aold)
+                                       anew = attr_find( e->e_attrs, mod->sm_desc );
+                               else
+                                       anew = aold;
+                               /* check for big multivalued attrs */
+                               if ( anew->a_numvals > mdb->mi_multi_hi )
+                                       anew->a_flags |= SLAP_ATTR_BIG_MULTI;
+                               if ( anew->a_flags & SLAP_ATTR_BIG_MULTI ) {
+                                       if (!mvc) {
+                                               err = mdb_cursor_open( tid, mdb->mi_dbis[MDB_ID2VAL], &mvc );
+                                               if (err) {
+mval_fail:                                     strncpy( textbuf, mdb_strerror( err ), textlen );
+                                                       err = LDAP_OTHER;
+                                                       break;
+                                               }
+                                       }
+                                       /* if prev was set, just add new values */
+                                       if (a_flags & SLAP_ATTR_BIG_MULTI ) {
+                                               anew = (Attribute *)mod;
+                                               /* Tweak nvals */
+                                               if (!anew->a_nvals)
+                                                       anew->a_nvals = anew->a_vals;
+                                       }
+                                       err = mdb_mval_put(op, mvc, e->e_id, anew);
+                                       if (a_flags & SLAP_ATTR_BIG_MULTI ) {
+                                               /* Undo nvals tweak */
+                                               if (anew->a_nvals == anew->a_vals)
+                                                       anew->a_nvals = NULL;
+                                       }
+                                       if ( err )
+                                               goto mval_fail;
+                               }
                        }
                        break;
 
@@ -148,16 +214,59 @@ int mdb_modify_internal(
                                break;
                        }
 
+                       softop = 0;
                        Debug(LDAP_DEBUG_ARGS,
                                "mdb_modify_internal: delete %s\n",
                                mod->sm_desc->ad_cname.bv_val, 0, 0);
+do_del:
                        err = modify_delete_values( e, mod, get_permissiveModify(op),
                                text, textbuf, textlen );
+
+                       if (softop) {
+                               mod->sm_op = SLAP_MOD_SOFTDEL;
+                               if ( err == LDAP_NO_SUCH_ATTRIBUTE ) {
+                                       err = LDAP_SUCCESS;
+                                       softop = 2;
+                               }
+                       }
+
                        if( err != LDAP_SUCCESS ) {
                                Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
                                        err, *text, 0);
                        } else {
-                               got_delete = 1;
+                               if (softop != 2)
+                                       got_delete = 1;
+                               /* check for big multivalued attrs */
+                               if (a_flags & SLAP_ATTR_BIG_MULTI) {
+                                       Attribute a_dummy;
+                                       if (!mvc) {
+                                               err = mdb_cursor_open( tid, mdb->mi_dbis[MDB_ID2VAL], &mvc );
+                                               if (err)
+                                                       goto mval_fail;
+                                       }
+                                       if ( mod->sm_numvals ) {
+                                               anew = attr_find( e->e_attrs, mod->sm_desc );
+                                               if ( anew ) {
+                                                       if ( anew->a_numvals < mdb->mi_multi_lo ) {
+                                                               anew->a_flags ^= SLAP_ATTR_BIG_MULTI;
+                                                               anew = NULL;
+                                                       } else {
+                                                               anew = (Attribute *)mod;
+                                                       }
+                                               }
+                                       } else {
+                                               anew = NULL;
+                                       }
+                                       if (!anew) {
+                                       /* delete all values */
+                                               anew = &a_dummy;
+                                               anew->a_desc = mod->sm_desc;
+                                               anew->a_numvals = 0;
+                                       }
+                                       err = mdb_mval_del( op, mvc, e->e_id, anew );
+                                       if ( err )
+                                               goto mval_fail;
+                               }
                        }
                        break;
 
@@ -172,6 +281,36 @@ int mdb_modify_internal(
                                        err, *text, 0);
                        } else {
                                got_delete = 1;
+                               if (a_flags & SLAP_ATTR_BIG_MULTI) {
+                                       Attribute a_dummy;
+                                       if (!mvc) {
+                                               err = mdb_cursor_open( tid, mdb->mi_dbis[MDB_ID2VAL], &mvc );
+                                               if (err)
+                                                       goto mval_fail;
+                                       }
+                                       /* delete all values */
+                                       anew = &a_dummy;
+                                       anew->a_desc = mod->sm_desc;
+                                       anew->a_numvals = 0;
+                                       err = mdb_mval_del( op, mvc, e->e_id, anew );
+                                       if (err)
+                                               goto mval_fail;
+                               }
+                               anew = attr_find( e->e_attrs, mod->sm_desc );
+                               if (mod->sm_numvals > mdb->mi_multi_hi) {
+                                       anew->a_flags |= SLAP_ATTR_BIG_MULTI;
+                                       if (!mvc) {
+                                               err = mdb_cursor_open( tid, mdb->mi_dbis[MDB_ID2VAL], &mvc );
+                                               if (err)
+                                                       goto mval_fail;
+                                       }
+                                       err = mdb_mval_put(op, mvc, e->e_id, anew);
+                                       if (err)
+                                               goto mval_fail;
+                               } else if (anew) {
+                                       /* revert back to normal attr */
+                                       anew->a_flags &= ~SLAP_ATTR_BIG_MULTI;
+                               }
                        }
                        break;
 
@@ -198,21 +337,9 @@ int mdb_modify_internal(
                         * We need to add index if necessary.
                         */
                        mod->sm_op = LDAP_MOD_ADD;
-
-                       err = modify_add_values( e, mod, get_permissiveModify(op),
-                               text, textbuf, textlen );
-
-                       mod->sm_op = SLAP_MOD_SOFTADD;
-
-                       if ( err == LDAP_TYPE_OR_VALUE_EXISTS ) {
-                               err = LDAP_SUCCESS;
-                       }
-
-                       if( err != LDAP_SUCCESS ) {
-                               Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
-                                       err, *text, 0);
-                       }
-                       break;
+                       softop = 1;
+                       chkpresent = 0;
+                       goto do_add;
 
                case SLAP_MOD_SOFTDEL:
                        Debug(LDAP_DEBUG_ARGS,
@@ -222,23 +349,8 @@ int mdb_modify_internal(
                         * We need to add index if necessary.
                         */
                        mod->sm_op = LDAP_MOD_DELETE;
-
-                       err = modify_delete_values( e, mod, get_permissiveModify(op),
-                               text, textbuf, textlen );
-
-                       mod->sm_op = SLAP_MOD_SOFTDEL;
-
-                       if ( err == LDAP_SUCCESS ) {
-                               got_delete = 1;
-                       } else if ( err == LDAP_NO_SUCH_ATTRIBUTE ) {
-                               err = LDAP_SUCCESS;
-                       }
-
-                       if( err != LDAP_SUCCESS ) {
-                               Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
-                                       err, *text, 0);
-                       }
-                       break;
+                       softop = 1;
+                       goto do_del;
 
                case SLAP_MOD_ADD_IF_NOT_PRESENT:
                        if ( attr_find( e->e_attrs, mod->sm_desc ) != NULL ) {
@@ -254,17 +366,9 @@ int mdb_modify_internal(
                         * We need to add index if necessary.
                         */
                        mod->sm_op = LDAP_MOD_ADD;
-
-                       err = modify_add_values( e, mod, get_permissiveModify(op),
-                               text, textbuf, textlen );
-
-                       mod->sm_op = SLAP_MOD_ADD_IF_NOT_PRESENT;
-
-                       if( err != LDAP_SUCCESS ) {
-                               Debug(LDAP_DEBUG_ARGS, "mdb_modify_internal: %d %s\n",
-                                       err, *text, 0);
-                       }
-                       break;
+                       softop = 0;
+                       chkpresent = 1;
+                       goto do_add;
 
                default:
                        Debug(LDAP_DEBUG_ANY, "mdb_modify_internal: invalid op %d\n",
@@ -458,6 +562,7 @@ mdb_modify( Operation *op, SlapReply *rs )
        LDAPControl **postread_ctrl = NULL;
        LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
        int num_ctrls = 0;
+       int numads = mdb->mi_numads;
 
        Debug( LDAP_DEBUG_ARGS, LDAP_XSTRING(mdb_modify) ": %s\n",
                op->o_req_dn.bv_val, 0, 0 );
@@ -635,12 +740,15 @@ mdb_modify( Operation *op, SlapReply *rs )
                LDAP_SLIST_REMOVE( &op->o_extra, &opinfo.moi_oe, OpExtra, oe_next );
                opinfo.moi_oe.oe_key = NULL;
                if( op->o_noop ) {
+                       mdb->mi_numads = numads;
                        mdb_txn_abort( txn );
                        rs->sr_err = LDAP_X_NO_OPERATION;
                        txn = NULL;
                        goto return_results;
                } else {
                        rs->sr_err = mdb_txn_commit( txn );
+                       if ( rs->sr_err )
+                               mdb->mi_numads = numads;
                        txn = NULL;
                }
        }
@@ -683,6 +791,7 @@ done:
 
        if( moi == &opinfo ) {
                if( txn != NULL ) {
+                       mdb->mi_numads = numads;
                        mdb_txn_abort( txn );
                }
                if ( opinfo.moi_oe.oe_key ) {