]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/delete.c
limit checking in syncrepl
[openldap] / servers / slapd / back-bdb / delete.c
1 /* delete.c - bdb backend delete routine */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2004 The OpenLDAP Foundation.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16
17 #include "portable.h"
18
19 #include <stdio.h>
20 #include <ac/string.h>
21
22 #include "back-bdb.h"
23 #include "external.h"
24
25 int
26 bdb_delete( Operation *op, SlapReply *rs )
27 {
28         struct bdb_info *bdb = (struct bdb_info *) op->o_bd->be_private;
29         Entry   *matched = NULL;
30         struct berval   pdn = {0, NULL};
31         Entry   *e = NULL;
32         Entry   *p = NULL;
33         EntryInfo       *ei = NULL, *eip = NULL;
34         int             manageDSAit = get_manageDSAit( op );
35         AttributeDescription *children = slap_schema.si_ad_children;
36         AttributeDescription *entry = slap_schema.si_ad_entry;
37         DB_TXN          *ltid = NULL, *lt2;
38         struct bdb_op_info opinfo;
39
40         u_int32_t       locker = 0;
41         DB_LOCK         lock, plock;
42
43         int             num_retries = 0;
44
45         Operation* ps_list;
46         int     rc;
47         EntryInfo   *suffix_ei;
48         Entry       *ctxcsn_e;
49         int         ctxcsn_added = 0;
50
51         LDAPControl *ctrls[SLAP_MAX_RESPONSE_CONTROLS];
52         int num_ctrls = 0;
53
54         int     parent_is_glue = 0;
55         int parent_is_leaf = 0;
56
57 #ifdef NEW_LOGGING
58         LDAP_LOG ( OPERATION, ARGS,  "==> bdb_delete: %s\n", op->o_req_dn.bv_val, 0, 0 );
59 #else
60         Debug( LDAP_DEBUG_ARGS, "==> bdb_delete: %s\n",
61                 op->o_req_dn.bv_val, 0, 0 );
62 #endif
63
64         if( 0 ) {
65 retry:  /* transaction retry */
66                 if( e != NULL ) {
67                         bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
68                         e = NULL;
69                 }
70 #ifdef NEW_LOGGING
71                 LDAP_LOG ( OPERATION, DETAIL1, 
72                         "==> bdb_delete: retrying...\n", 0, 0, 0 );
73 #else
74                 Debug( LDAP_DEBUG_TRACE, "==> bdb_delete: retrying...\n",
75                         0, 0, 0 );
76 #endif
77                 rs->sr_err = TXN_ABORT( ltid );
78                 ltid = NULL;
79                 op->o_private = NULL;
80                 op->o_do_not_cache = opinfo.boi_acl_cache;
81                 if( rs->sr_err != 0 ) {
82                         rs->sr_err = LDAP_OTHER;
83                         rs->sr_text = "internal error";
84                         goto return_results;
85                 }
86                 parent_is_glue = 0;
87                 parent_is_leaf = 0;
88                 ldap_pvt_thread_yield();
89                 bdb_trans_backoff( ++num_retries );
90         }
91
92         /* begin transaction */
93         rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, NULL, &ltid, 
94                 bdb->bi_db_opflags );
95         rs->sr_text = NULL;
96         if( rs->sr_err != 0 ) {
97 #ifdef NEW_LOGGING
98                 LDAP_LOG ( OPERATION, ERR, 
99                         "==> bdb_delete: txn_begin failed: %s (%d)\n",
100                         db_strerror(rs->sr_err), rs->sr_err, 0 );
101 #else
102                 Debug( LDAP_DEBUG_TRACE,
103                         "bdb_delete: txn_begin failed: %s (%d)\n",
104                         db_strerror(rs->sr_err), rs->sr_err, 0 );
105 #endif
106                 rs->sr_err = LDAP_OTHER;
107                 rs->sr_text = "internal error";
108                 goto return_results;
109         }
110
111         locker = TXN_ID ( ltid );
112
113         opinfo.boi_bdb = op->o_bd;
114         opinfo.boi_txn = ltid;
115         opinfo.boi_locker = locker;
116         opinfo.boi_err = 0;
117         opinfo.boi_acl_cache = op->o_do_not_cache;
118         op->o_private = &opinfo;
119
120         if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
121                 dnParent( &op->o_req_ndn, &pdn );
122         }
123
124         /* get entry */
125         rs->sr_err = bdb_dn2entry( op, ltid, &op->o_req_ndn, &ei, 1,
126                 locker, &lock );
127
128         switch( rs->sr_err ) {
129         case 0:
130         case DB_NOTFOUND:
131                 break;
132         case DB_LOCK_DEADLOCK:
133         case DB_LOCK_NOTGRANTED:
134                 goto retry;
135         case LDAP_BUSY:
136                 rs->sr_text = "ldap server busy";
137                 goto return_results;
138         default:
139                 rs->sr_err = LDAP_OTHER;
140                 rs->sr_text = "internal error";
141                 goto return_results;
142         }
143
144         if ( rs->sr_err == 0 ) {
145                 e = ei->bei_e;
146                 eip = ei->bei_parent;
147         } else {
148                 matched = ei->bei_e;
149         }
150
151         /* FIXME : dn2entry() should return non-glue entry */
152         if ( e == NULL || ( !manageDSAit && is_entry_glue( e ))) {
153 #ifdef NEW_LOGGING
154                 LDAP_LOG ( OPERATION, ARGS, 
155                         "<=- bdb_delete: no such object %s\n", op->o_req_dn.bv_val, 0, 0);
156 #else
157                 Debug( LDAP_DEBUG_ARGS,
158                         "<=- bdb_delete: no such object %s\n",
159                         op->o_req_dn.bv_val, 0, 0);
160 #endif
161
162                 if ( matched != NULL ) {
163                         rs->sr_matched = ch_strdup( matched->e_dn );
164                         rs->sr_ref = is_entry_referral( matched )
165                                 ? get_entry_referrals( op, matched )
166                                 : NULL;
167                         bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, matched);
168                         matched = NULL;
169
170                 } else {
171                         BerVarray deref = NULL;
172                         if ( !LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
173                                 syncinfo_t *si;
174                                 LDAP_STAILQ_FOREACH( si, &op->o_bd->be_syncinfo, si_next ) {
175                                         struct berval tmpbv;
176                                         ber_dupbv( &tmpbv, &si->si_provideruri_bv[0] );
177                                         ber_bvarray_add( &deref, &tmpbv );
178                                 }
179                         } else {
180                                 deref = default_referral;
181                         }
182                         rs->sr_ref = referral_rewrite( deref, NULL, &op->o_req_dn,
183                                 LDAP_SCOPE_DEFAULT );
184                 }
185
186                 rs->sr_err = LDAP_REFERRAL;
187                 send_ldap_result( op, rs );
188
189                 if ( rs->sr_ref != default_referral ) {
190                         ber_bvarray_free( rs->sr_ref );
191                 }
192                 free( (char *)rs->sr_matched );
193                 rs->sr_ref = NULL;
194                 rs->sr_matched = NULL;
195
196                 rs->sr_err = -1;
197                 goto done;
198         }
199
200         bdb_cache_find_id( op, ltid, eip->bei_id, &eip, 0, locker, &plock );
201         if ( eip ) p = eip->bei_e;
202
203         if ( pdn.bv_len != 0 ) {
204                 if( p == NULL || !bvmatch( &pdn, &p->e_nname )) {
205 #ifdef NEW_LOGGING
206                         LDAP_LOG ( OPERATION, DETAIL1, 
207                                 "<=- bdb_delete: parent does not exist\n", 0, 0, 0 );
208 #else
209                         Debug( LDAP_DEBUG_TRACE,
210                                 "<=- bdb_delete: parent does not exist\n",
211                                 0, 0, 0);
212 #endif
213                         rs->sr_err = LDAP_OTHER;
214                         rs->sr_text = "could not locate parent of entry";
215                         goto return_results;
216                 }
217
218                 /* check parent for "children" acl */
219                 rs->sr_err = access_allowed( op, p,
220                         children, NULL, ACL_WRITE, NULL );
221
222                 bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
223                 p = NULL;
224
225                 if ( !rs->sr_err  ) {
226                         switch( opinfo.boi_err ) {
227                         case DB_LOCK_DEADLOCK:
228                         case DB_LOCK_NOTGRANTED:
229                                 goto retry;
230                         }
231
232 #ifdef NEW_LOGGING
233                         LDAP_LOG ( OPERATION, DETAIL1, 
234                                 "<=- bdb_delete: no write access to parent\n", 0, 0, 0 );
235 #else
236                         Debug( LDAP_DEBUG_TRACE,
237                                 "<=- bdb_delete: no write access to parent\n",
238                                 0, 0, 0 );
239 #endif
240                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
241                         rs->sr_text = "no write access to parent";
242                         goto return_results;
243                 }
244
245         } else {
246                 /* no parent, must be root to delete */
247                 if( ! be_isroot( op ) ) {
248                         if ( be_issuffix( op->o_bd, (struct berval *)&slap_empty_bv )
249                                 || be_shadow_update( op ) ) {
250                                 p = (Entry *)&slap_entry_root;
251
252                                 /* check parent for "children" acl */
253                                 rs->sr_err = access_allowed( op, p,
254                                         children, NULL, ACL_WRITE, NULL );
255
256                                 p = NULL;
257
258                                 if ( !rs->sr_err  ) {
259                                         switch( opinfo.boi_err ) {
260                                         case DB_LOCK_DEADLOCK:
261                                         case DB_LOCK_NOTGRANTED:
262                                                 goto retry;
263                                         }
264
265 #ifdef NEW_LOGGING
266                                         LDAP_LOG ( OPERATION, DETAIL1, 
267                                                 "<=- bdb_delete: no access to parent\n", 0, 0, 0 );
268 #else
269                                         Debug( LDAP_DEBUG_TRACE,
270                                                 "<=- bdb_delete: no access "
271                                                 "to parent\n", 0, 0, 0 );
272 #endif
273                                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
274                                         rs->sr_text = "no write access to parent";
275                                         goto return_results;
276                                 }
277
278                         } else {
279 #ifdef NEW_LOGGING
280                                 LDAP_LOG ( OPERATION, DETAIL1, 
281                                         "<=- bdb_delete: no parent and not root\n", 0, 0, 0 );
282 #else
283                                 Debug( LDAP_DEBUG_TRACE,
284                                         "<=- bdb_delete: no parent "
285                                         "and not root\n", 0, 0, 0);
286 #endif
287                                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
288                                 goto return_results;
289                         }
290                 }
291         }
292
293         if ( get_assert( op ) &&
294                 ( test_filter( op, e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
295         {
296                 rs->sr_err = LDAP_ASSERTION_FAILED;
297                 goto return_results;
298         }
299
300         rs->sr_err = access_allowed( op, e,
301                 entry, NULL, ACL_WRITE, NULL );
302
303         if ( !rs->sr_err  ) {
304                 switch( opinfo.boi_err ) {
305                 case DB_LOCK_DEADLOCK:
306                 case DB_LOCK_NOTGRANTED:
307                         goto retry;
308                 }
309
310 #ifdef NEW_LOGGING
311                 LDAP_LOG ( OPERATION, DETAIL1, 
312                         "<=- bdb_delete: no write access to entry\n", 0, 0, 0 );
313 #else
314                 Debug( LDAP_DEBUG_TRACE,
315                         "<=- bdb_delete: no write access to entry\n",
316                         0, 0, 0 );
317 #endif
318                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
319                 rs->sr_text = "no write access to entry";
320                 goto return_results;
321         }
322
323         if ( !manageDSAit && is_entry_referral( e ) ) {
324                 /* entry is a referral, don't allow delete */
325                 rs->sr_ref = get_entry_referrals( op, e );
326
327 #ifdef NEW_LOGGING
328                 LDAP_LOG ( OPERATION, DETAIL1, 
329                         "<=- bdb_delete: entry is referral\n", 0, 0, 0 );
330 #else
331                 Debug( LDAP_DEBUG_TRACE,
332                         "bdb_delete: entry is referral\n", 0, 0, 0 );
333 #endif
334
335                 rs->sr_err = LDAP_REFERRAL;
336                 rs->sr_matched = e->e_name.bv_val;
337                 send_ldap_result( op, rs );
338
339                 ber_bvarray_free( rs->sr_ref );
340                 rs->sr_ref = NULL;
341                 rs->sr_matched = NULL;
342
343                 rs->sr_err = 1;
344                 goto done;
345         }
346
347         /* pre-read */
348         if( op->o_preread ) {
349                 if( slap_read_controls( op, rs, e,
350                         &slap_pre_read_bv, &ctrls[num_ctrls] ) )
351                 {
352 #ifdef NEW_LOGGING
353                         LDAP_LOG ( OPERATION, DETAIL1, 
354                                 "<=- bdb_delete: pre-read failed!\n", 0, 0, 0 );
355 #else
356                         Debug( LDAP_DEBUG_TRACE,
357                                 "<=- bdb_delete: pre-read failed!\n", 0, 0, 0 );
358 #endif
359                         goto return_results;
360                 }
361                 ctrls[++num_ctrls] = NULL;
362         }
363
364         /* nested transaction */
365         rs->sr_err = TXN_BEGIN( bdb->bi_dbenv, ltid, &lt2, 
366                 bdb->bi_db_opflags );
367         rs->sr_text = NULL;
368         if( rs->sr_err != 0 ) {
369 #ifdef NEW_LOGGING
370                 LDAP_LOG ( OPERATION, ERR, 
371                         "bdb_delete: txn_begin(2) failed: %s (%d)\n", db_strerror(rs->sr_err), rs->sr_err, 0 );
372 #else
373                 Debug( LDAP_DEBUG_TRACE,
374                         "bdb_delete: txn_begin(2) failed: %s (%d)\n",
375                         db_strerror(rs->sr_err), rs->sr_err, 0 );
376 #endif
377                 rs->sr_err = LDAP_OTHER;
378                 rs->sr_text = "internal error";
379                 goto return_results;
380         }
381
382         /* Can't do it if we have kids */
383         rs->sr_err = bdb_cache_children( op, lt2, e );
384         if( rs->sr_err != DB_NOTFOUND ) {
385                 switch( rs->sr_err ) {
386                 case DB_LOCK_DEADLOCK:
387                 case DB_LOCK_NOTGRANTED:
388                         goto retry;
389                 case 0:
390 #ifdef NEW_LOGGING
391                         LDAP_LOG ( OPERATION, DETAIL1, 
392                                 "<=- bdb_delete: non-leaf %s\n", op->o_req_dn.bv_val, 0, 0 );
393 #else
394                         Debug(LDAP_DEBUG_ARGS,
395                                 "<=- bdb_delete: non-leaf %s\n",
396                                 op->o_req_dn.bv_val, 0, 0);
397 #endif
398                         rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
399                         rs->sr_text = "subtree delete not supported";
400                         break;
401                 default:
402 #ifdef NEW_LOGGING
403                         LDAP_LOG ( OPERATION, ERR, 
404                                 "<=- bdb_delete: has_children failed %s (%d)\n",
405                                 db_strerror(rs->sr_err), rs->sr_err, 0 );
406 #else
407                         Debug(LDAP_DEBUG_ARGS,
408                                 "<=- bdb_delete: has_children failed: %s (%d)\n",
409                                 db_strerror(rs->sr_err), rs->sr_err, 0 );
410 #endif
411                         rs->sr_err = LDAP_OTHER;
412                         rs->sr_text = "internal error";
413                 }
414                 goto return_results;
415         }
416
417         /* delete from dn2id */
418         rs->sr_err = bdb_dn2id_delete( op, lt2, eip, e );
419         if ( rs->sr_err != 0 ) {
420 #ifdef NEW_LOGGING
421                 LDAP_LOG ( OPERATION, ERR, 
422                         "<=- bdb_delete: dn2id failed: %s (%d)\n",
423                         db_strerror(rs->sr_err), rs->sr_err, 0 );
424 #else
425                 Debug(LDAP_DEBUG_TRACE,
426                         "<=- bdb_delete: dn2id failed: %s (%d)\n",
427                         db_strerror(rs->sr_err), rs->sr_err, 0 );
428 #endif
429                 switch( rs->sr_err ) {
430                 case DB_LOCK_DEADLOCK:
431                 case DB_LOCK_NOTGRANTED:
432                         goto retry;
433                 }
434                 rs->sr_text = "DN index delete failed";
435                 rs->sr_err = LDAP_OTHER;
436                 goto return_results;
437         }
438
439         /* delete from id2entry */
440         rs->sr_err = bdb_id2entry_delete( op->o_bd, lt2, e );
441         if ( rs->sr_err != 0 ) {
442 #ifdef NEW_LOGGING
443                 LDAP_LOG ( OPERATION, ERR, 
444                         "<=- bdb_delete: id2entry failed: %s (%d)\n", 
445                         db_strerror(rs->sr_err), rs->sr_err, 0 );
446 #else
447                 Debug(LDAP_DEBUG_TRACE,
448                         "<=- bdb_delete: id2entry failed: %s (%d)\n",
449                         db_strerror(rs->sr_err), rs->sr_err, 0 );
450 #endif
451                 switch( rs->sr_err ) {
452                 case DB_LOCK_DEADLOCK:
453                 case DB_LOCK_NOTGRANTED:
454                         goto retry;
455                 }
456                 rs->sr_text = "entry delete failed";
457                 rs->sr_err = LDAP_OTHER;
458                 goto return_results;
459         }
460
461         /* delete indices for old attributes */
462         rs->sr_err = bdb_index_entry_del( op, lt2, e );
463         if ( rs->sr_err != LDAP_SUCCESS ) {
464 #ifdef NEW_LOGGING
465                 LDAP_LOG ( OPERATION, ERR, 
466                         "<=- bdb_delete: index failed: %s (%d)\n", 
467                         db_strerror(rs->sr_err), rs->sr_err, 0 );
468 #else
469                 Debug( LDAP_DEBUG_TRACE,
470                         "<=- bdb_delete: index failed: %s (%d)\n", 
471                         db_strerror(rs->sr_err), rs->sr_err, 0 );
472 #endif
473                 switch( rs->sr_err ) {
474                 case DB_LOCK_DEADLOCK:
475                 case DB_LOCK_NOTGRANTED:
476                         goto retry;
477                 }
478                 rs->sr_text = "entry index delete failed";
479                 rs->sr_err = LDAP_OTHER;
480                 goto return_results;
481         }
482
483         bdb_cache_find_id( op, lt2, eip->bei_id, &eip, 0, locker, &plock );
484         if ( eip ) p = eip->bei_e;
485         if ( pdn.bv_len != 0 ) {
486                 parent_is_glue = is_entry_glue(p);
487                 rs->sr_err = bdb_cache_children( op, lt2, p );
488                 if ( rs->sr_err != DB_NOTFOUND ) {
489                         switch( rs->sr_err ) {
490                         case DB_LOCK_DEADLOCK:
491                         case DB_LOCK_NOTGRANTED:
492                                 goto retry;
493                         case 0:
494                                 break;
495                         default:
496 #ifdef NEW_LOGGING
497                                 LDAP_LOG ( OPERATION, ERR, 
498                                         "<=- bdb_delete: has_children failed %s (%d)\n",
499                                         db_strerror(rs->sr_err), rs->sr_err, 0 );
500 #else
501                                 Debug(LDAP_DEBUG_ARGS,
502                                         "<=- bdb_delete: has_children failed: %s (%d)\n",
503                                         db_strerror(rs->sr_err), rs->sr_err, 0 );
504 #endif
505                                 rs->sr_err = LDAP_OTHER;
506                                 rs->sr_text = "internal error";
507                                 goto return_results;
508                         }
509                         parent_is_leaf = 1;
510                 }
511                 bdb_unlocked_cache_return_entry_r(&bdb->bi_cache, p);
512                 p = NULL;
513         }
514
515         if ( TXN_COMMIT( lt2, 0 ) != 0 ) {
516                 rs->sr_err = LDAP_OTHER;
517                 rs->sr_text = "txn_commit(2) failed";
518                 goto return_results;
519         }
520
521 #if 0   /* Do we want to reclaim deleted IDs? */
522         ldap_pvt_thread_mutex_lock( &bdb->bi_lastid_mutex );
523         if ( e->e_id == bdb->bi_lastid ) {
524                 bdb_last_id( op->o_bd, ltid );
525         }
526         ldap_pvt_thread_mutex_unlock( &bdb->bi_lastid_mutex );
527 #endif
528
529         if ( LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
530                 rc = bdb_csn_commit( op, rs, ltid, ei, &suffix_ei,
531                         &ctxcsn_e, &ctxcsn_added, locker );
532                 switch ( rc ) {
533                 case BDB_CSN_ABORT :
534                         goto return_results;
535                 case BDB_CSN_RETRY :
536                         goto retry;
537                 }
538         }
539
540         if( op->o_noop ) {
541                 if ( ( rs->sr_err = TXN_ABORT( ltid ) ) != 0 ) {
542                         rs->sr_text = "txn_abort (no-op) failed";
543                 } else {
544                         rs->sr_err = LDAP_NO_OPERATION;
545                         goto return_results;
546                 }
547         } else {
548                 rc = bdb_cache_delete( &bdb->bi_cache, e, bdb->bi_dbenv,
549                         locker, &lock );
550                 switch( rc ) {
551                 case DB_LOCK_DEADLOCK:
552                 case DB_LOCK_NOTGRANTED:
553                         goto retry;
554                 }
555
556                 if ( LDAP_STAILQ_EMPTY( &op->o_bd->be_syncinfo )) {
557                         if ( ctxcsn_added ) {
558                                 bdb_cache_add( bdb, suffix_ei,
559                                         ctxcsn_e, (struct berval *)&slap_ldapsync_cn_bv, locker );
560                         }
561                 }
562
563                 if ( rs->sr_err == LDAP_SUCCESS && !op->o_no_psearch ) {
564                         ldap_pvt_thread_rdwr_rlock( &bdb->bi_pslist_rwlock );
565                         LDAP_LIST_FOREACH( ps_list, &bdb->bi_psearch_list, o_ps_link ) {
566                                 bdb_psearch( op, rs, ps_list, e, LDAP_PSEARCH_BY_DELETE );
567                         }
568                         ldap_pvt_thread_rdwr_runlock( &bdb->bi_pslist_rwlock );
569                 }
570
571                 rs->sr_err = TXN_COMMIT( ltid, 0 );
572         }
573         ltid = NULL;
574         op->o_private = NULL;
575
576         if( rs->sr_err != 0 ) {
577 #ifdef NEW_LOGGING
578                 LDAP_LOG ( OPERATION, ERR, 
579                         "bdb_delete: txn_%s failed: %s (%d)\n",
580                         op->o_noop ? "abort (no-op)" : "commit",
581                         db_strerror(rs->sr_err), rs->sr_err );
582 #else
583                 Debug( LDAP_DEBUG_TRACE,
584                         "bdb_delete: txn_%s failed: %s (%d)\n",
585                         op->o_noop ? "abort (no-op)" : "commit",
586                         db_strerror(rs->sr_err), rs->sr_err );
587 #endif
588                 rs->sr_err = LDAP_OTHER;
589                 rs->sr_text = "commit failed";
590
591                 goto return_results;
592         }
593
594 #ifdef NEW_LOGGING
595         LDAP_LOG ( OPERATION, RESULTS, 
596                 "bdb_delete: deleted%s id=%08lx db=\"%s\"\n",
597                 op->o_noop ? " (no-op)" : "",
598                 e->e_id, e->e_dn );
599 #else
600         Debug( LDAP_DEBUG_TRACE,
601                 "bdb_delete: deleted%s id=%08lx dn=\"%s\"\n",
602                 op->o_noop ? " (no-op)" : "",
603                 e->e_id, e->e_dn );
604 #endif
605         rs->sr_err = LDAP_SUCCESS;
606         rs->sr_text = NULL;
607         if( num_ctrls ) rs->sr_ctrls = ctrls;
608
609 return_results:
610         send_ldap_result( op, rs );
611
612         if( rs->sr_err == LDAP_SUCCESS && bdb->bi_txn_cp ) {
613                 ldap_pvt_thread_yield();
614                 TXN_CHECKPOINT( bdb->bi_dbenv,
615                         bdb->bi_txn_cp_kbyte, bdb->bi_txn_cp_min, 0 );
616         }
617
618         if ( rs->sr_err == LDAP_SUCCESS && parent_is_glue && parent_is_leaf ) {
619                 op->o_delete_glue_parent = 1;
620         }
621
622 done:
623         /* free entry */
624         if( e != NULL ) {
625                 if ( rs->sr_err == LDAP_SUCCESS ) {
626                         /* Free the EntryInfo and the Entry */
627                         bdb_cache_delete_cleanup( &bdb->bi_cache, e );
628                 } else {
629                         bdb_unlocked_cache_return_entry_w(&bdb->bi_cache, e);
630                 }
631         }
632
633         if( ltid != NULL ) {
634                 TXN_ABORT( ltid );
635                 op->o_private = NULL;
636         }
637
638         return rs->sr_err;
639 }