1 /*	$NetBSD: delete.c,v 1.3 2021/08/14 16:15:01 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 1999-2021 The OpenLDAP Foundation.
7  * Portions Copyright 1999 Dmitry Kovalev.
8  * Portions Copyright 2002 Pierangelo Masarati.
9  * All rights reserved.
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted only as authorized by the OpenLDAP
13  * Public License.
14  *
15  * A copy of this license is available in the file LICENSE in the
16  * top-level directory of the distribution or, alternatively, at
17  * <http://www.OpenLDAP.org/license.html>.
18  */
19 /* ACKNOWLEDGEMENTS:
20  * This work was initially developed by Dmitry Kovalev for inclusion
21  * by OpenLDAP Software.  Additional significant contributors include
22  * Pierangelo Masarati.
23  */
24 
25 #include <sys/cdefs.h>
26 __RCSID("$NetBSD: delete.c,v 1.3 2021/08/14 16:15:01 christos Exp $");
27 
28 #include "portable.h"
29 
30 #include <stdio.h>
31 #include <sys/types.h>
32 #include "ac/string.h"
33 
34 #include "slap.h"
35 #include "proto-sql.h"
36 
37 typedef struct backsql_delete_attr_t {
38 	Operation 		*op;
39 	SlapReply		*rs;
40 	SQLHDBC			dbh;
41 	backsql_entryID		*e_id;
42 } backsql_delete_attr_t;
43 
44 static int
backsql_delete_attr_f(void * v_at,void * v_bda)45 backsql_delete_attr_f( void *v_at, void *v_bda )
46 {
47 	backsql_at_map_rec	*at = (backsql_at_map_rec *)v_at;
48 	backsql_delete_attr_t	*bda = (backsql_delete_attr_t *)v_bda;
49 	int			rc;
50 
51 	rc = backsql_modify_delete_all_values( bda->op,
52 			bda->rs, bda->dbh, bda->e_id, at );
53 
54 	if ( rc != LDAP_SUCCESS ) {
55 		return BACKSQL_AVL_STOP;
56 	}
57 
58 	return BACKSQL_AVL_CONTINUE;
59 }
60 
61 static int
backsql_delete_all_attrs(Operation * op,SlapReply * rs,SQLHDBC dbh,backsql_entryID * eid)62 backsql_delete_all_attrs(
63 	Operation 		*op,
64 	SlapReply		*rs,
65 	SQLHDBC			dbh,
66 	backsql_entryID		*eid )
67 {
68 	backsql_delete_attr_t	bda;
69 	int			rc;
70 
71 	bda.op = op;
72 	bda.rs = rs;
73 	bda.dbh = dbh;
74 	bda.e_id = eid;
75 
76 	rc = ldap_avl_apply( eid->eid_oc->bom_attrs, backsql_delete_attr_f, &bda,
77 			BACKSQL_AVL_STOP, AVL_INORDER );
78 	if ( rc == BACKSQL_AVL_STOP ) {
79 		return rs->sr_err;
80 	}
81 
82 	return LDAP_SUCCESS;
83 }
84 
85 static int
backsql_delete_int(Operation * op,SlapReply * rs,SQLHDBC dbh,SQLHSTMT * sthp,backsql_entryID * eid,Entry ** ep)86 backsql_delete_int(
87 	Operation	*op,
88 	SlapReply	*rs,
89 	SQLHDBC		dbh,
90 	SQLHSTMT	*sthp,
91 	backsql_entryID	*eid,
92 	Entry		**ep )
93 {
94 	backsql_info 		*bi = (backsql_info*)op->o_bd->be_private;
95 	SQLHSTMT		sth = SQL_NULL_HSTMT;
96 	RETCODE			rc;
97 	int			prc = LDAP_SUCCESS;
98 	/* first parameter no */
99 	SQLUSMALLINT		pno = 0;
100 
101 	sth = *sthp;
102 
103 	/* ldap_avl_apply ... */
104 	rs->sr_err = backsql_delete_all_attrs( op, rs, dbh, eid );
105 	if ( rs->sr_err != LDAP_SUCCESS ) {
106 		goto done;
107 	}
108 
109 	rc = backsql_Prepare( dbh, &sth, eid->eid_oc->bom_delete_proc, 0 );
110 	if ( rc != SQL_SUCCESS ) {
111 		Debug( LDAP_DEBUG_TRACE,
112 			"   backsql_delete(): "
113 			"error preparing delete query\n" );
114 		backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
115 
116 		rs->sr_err = LDAP_OTHER;
117 		rs->sr_text = "SQL-backend error";
118 		*ep = NULL;
119 		goto done;
120 	}
121 
122 	if ( BACKSQL_IS_DEL( eid->eid_oc->bom_expect_return ) ) {
123 		pno = 1;
124 		rc = backsql_BindParamInt( sth, 1, SQL_PARAM_OUTPUT, &prc );
125 		if ( rc != SQL_SUCCESS ) {
126 			Debug( LDAP_DEBUG_TRACE,
127 				"   backsql_delete(): "
128 				"error binding output parameter for objectClass %s\n",
129 				eid->eid_oc->bom_oc->soc_cname.bv_val );
130 			backsql_PrintErrors( bi->sql_db_env, dbh,
131 				sth, rc );
132 			SQLFreeStmt( sth, SQL_DROP );
133 
134 			rs->sr_text = "SQL-backend error";
135 			rs->sr_err = LDAP_OTHER;
136 			*ep = NULL;
137 			goto done;
138 		}
139 	}
140 
141 	rc = backsql_BindParamID( sth, pno + 1, SQL_PARAM_INPUT, &eid->eid_keyval );
142 	if ( rc != SQL_SUCCESS ) {
143 		Debug( LDAP_DEBUG_TRACE,
144 			"   backsql_delete(): "
145 			"error binding keyval parameter for objectClass %s\n",
146 			eid->eid_oc->bom_oc->soc_cname.bv_val );
147 		backsql_PrintErrors( bi->sql_db_env, dbh,
148 			sth, rc );
149 		SQLFreeStmt( sth, SQL_DROP );
150 
151 		rs->sr_text = "SQL-backend error";
152 		rs->sr_err = LDAP_OTHER;
153 		*ep = NULL;
154 		goto done;
155 	}
156 
157 	rc = SQLExecute( sth );
158 	if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
159 		rs->sr_err = LDAP_SUCCESS;
160 
161 	} else {
162 		Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
163 			"delete_proc execution failed (rc=%d, prc=%d)\n",
164 			rc, prc );
165 
166 
167 		if ( prc != LDAP_SUCCESS ) {
168 			/* SQL procedure executed fine
169 			 * but returned an error */
170 			rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
171 
172 		} else {
173 			backsql_PrintErrors( bi->sql_db_env, dbh,
174 					sth, rc );
175 			rs->sr_err = LDAP_OTHER;
176 		}
177 		SQLFreeStmt( sth, SQL_DROP );
178 		goto done;
179 	}
180 	SQLFreeStmt( sth, SQL_DROP );
181 
182 	/* delete "auxiliary" objectClasses, if any... */
183 	rc = backsql_Prepare( dbh, &sth, bi->sql_delobjclasses_stmt, 0 );
184 	if ( rc != SQL_SUCCESS ) {
185 		Debug( LDAP_DEBUG_TRACE,
186 			"   backsql_delete(): "
187 			"error preparing ldap_entry_objclasses delete query\n" );
188 		backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
189 
190 		rs->sr_err = LDAP_OTHER;
191 		rs->sr_text = "SQL-backend error";
192 		*ep = NULL;
193 		goto done;
194 	}
195 
196 	rc = backsql_BindParamID( sth, 1, SQL_PARAM_INPUT, &eid->eid_id );
197 	if ( rc != SQL_SUCCESS ) {
198 		Debug( LDAP_DEBUG_TRACE,
199 			"   backsql_delete(): "
200 			"error binding auxiliary objectClasses "
201 			"entry ID parameter for objectClass %s\n",
202 			eid->eid_oc->bom_oc->soc_cname.bv_val );
203 		backsql_PrintErrors( bi->sql_db_env, dbh,
204 			sth, rc );
205 		SQLFreeStmt( sth, SQL_DROP );
206 
207 		rs->sr_text = "SQL-backend error";
208 		rs->sr_err = LDAP_OTHER;
209 		*ep = NULL;
210 		goto done;
211 	}
212 
213 	rc = SQLExecute( sth );
214 	switch ( rc ) {
215 	case SQL_NO_DATA:
216 		/* apparently there were no "auxiliary" objectClasses
217 		 * for this entry... */
218 	case SQL_SUCCESS:
219 		break;
220 
221 	default:
222 		Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
223 			"failed to delete record from ldap_entry_objclasses\n" );
224 		backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
225 		SQLFreeStmt( sth, SQL_DROP );
226 		rs->sr_err = LDAP_OTHER;
227 		rs->sr_text = "SQL-backend error";
228 		*ep = NULL;
229 		goto done;
230 	}
231 	SQLFreeStmt( sth, SQL_DROP );
232 
233 	/* delete entry... */
234 	rc = backsql_Prepare( dbh, &sth, bi->sql_delentry_stmt, 0 );
235 	if ( rc != SQL_SUCCESS ) {
236 		Debug( LDAP_DEBUG_TRACE,
237 			"   backsql_delete(): "
238 			"error preparing ldap_entries delete query\n" );
239 		backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
240 
241 		rs->sr_err = LDAP_OTHER;
242 		rs->sr_text = "SQL-backend error";
243 		*ep = NULL;
244 		goto done;
245 	}
246 
247 	rc = backsql_BindParamID( sth, 1, SQL_PARAM_INPUT, &eid->eid_id );
248 	if ( rc != SQL_SUCCESS ) {
249 		Debug( LDAP_DEBUG_TRACE,
250 			"   backsql_delete(): "
251 			"error binding entry ID parameter "
252 			"for objectClass %s\n",
253 			eid->eid_oc->bom_oc->soc_cname.bv_val );
254 		backsql_PrintErrors( bi->sql_db_env, dbh,
255 			sth, rc );
256 		SQLFreeStmt( sth, SQL_DROP );
257 
258 		rs->sr_text = "SQL-backend error";
259 		rs->sr_err = LDAP_OTHER;
260 		*ep = NULL;
261 		goto done;
262 	}
263 
264 	rc = SQLExecute( sth );
265 	if ( rc != SQL_SUCCESS ) {
266 		Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
267 			"failed to delete record from ldap_entries\n" );
268 		backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
269 		SQLFreeStmt( sth, SQL_DROP );
270 		rs->sr_err = LDAP_OTHER;
271 		rs->sr_text = "SQL-backend error";
272 		*ep = NULL;
273 		goto done;
274 	}
275 	SQLFreeStmt( sth, SQL_DROP );
276 
277 	rs->sr_err = LDAP_SUCCESS;
278 	*ep = NULL;
279 
280 done:;
281 	*sthp = sth;
282 
283 	return rs->sr_err;
284 }
285 
286 typedef struct backsql_tree_delete_t {
287 	Operation	*btd_op;
288 	int		btd_rc;
289 	backsql_entryID	*btd_eid;
290 } backsql_tree_delete_t;
291 
292 static int
backsql_tree_delete_search_cb(Operation * op,SlapReply * rs)293 backsql_tree_delete_search_cb( Operation *op, SlapReply *rs )
294 {
295 	if ( rs->sr_type == REP_SEARCH ) {
296 		backsql_tree_delete_t	*btd;
297 		backsql_entryID		*eid;
298 
299 		btd = (backsql_tree_delete_t *)op->o_callback->sc_private;
300 
301 		if ( !access_allowed( btd->btd_op, rs->sr_entry,
302 			slap_schema.si_ad_entry, NULL, ACL_WDEL, NULL )
303 			|| !access_allowed( btd->btd_op, rs->sr_entry,
304 			slap_schema.si_ad_children, NULL, ACL_WDEL, NULL ) )
305 		{
306 			btd->btd_rc = LDAP_INSUFFICIENT_ACCESS;
307 			return rs->sr_err = LDAP_UNAVAILABLE;
308 		}
309 
310 		assert( rs->sr_entry != NULL );
311 		assert( rs->sr_entry->e_private != NULL );
312 
313 		eid = (backsql_entryID *)rs->sr_entry->e_private;
314 		assert( eid->eid_oc != NULL );
315 		if ( eid->eid_oc == NULL || eid->eid_oc->bom_delete_proc == NULL ) {
316 			btd->btd_rc = LDAP_UNWILLING_TO_PERFORM;
317 			return rs->sr_err = LDAP_UNAVAILABLE;
318 		}
319 
320 		eid = backsql_entryID_dup( eid, op->o_tmpmemctx );
321 		eid->eid_next = btd->btd_eid;
322 		btd->btd_eid = eid;
323 	}
324 
325 	return 0;
326 }
327 
328 static int
backsql_tree_delete(Operation * op,SlapReply * rs,SQLHDBC dbh,SQLHSTMT * sthp)329 backsql_tree_delete(
330 	Operation	*op,
331 	SlapReply	*rs,
332 	SQLHDBC		dbh,
333 	SQLHSTMT	*sthp )
334 {
335 	Operation		op2 = *op;
336 	slap_callback		sc = { 0 };
337 	SlapReply		rs2 = { REP_RESULT };
338 	backsql_tree_delete_t	btd = { 0 };
339 
340 	int			rc;
341 
342 	/*
343 	 * - perform an internal subtree search as the rootdn
344 	 * - for each entry
345 	 *	- check access
346 	 *	- check objectClass and delete method(s)
347 	 * - for each entry
348 	 *	- delete
349 	 * - if successful, commit
350 	 */
351 
352 	op2.o_tag = LDAP_REQ_SEARCH;
353 	op2.o_protocol = LDAP_VERSION3;
354 
355 	btd.btd_op = op;
356 	sc.sc_private = &btd;
357 	sc.sc_response = backsql_tree_delete_search_cb;
358 	op2.o_callback = &sc;
359 
360 	op2.o_dn = op->o_bd->be_rootdn;
361 	op2.o_ndn = op->o_bd->be_rootndn;
362 
363 	op2.o_managedsait = SLAP_CONTROL_CRITICAL;
364 
365 	op2.ors_scope = LDAP_SCOPE_SUBTREE;
366 	op2.ors_deref = LDAP_DEREF_NEVER;
367 	op2.ors_slimit = SLAP_NO_LIMIT;
368 	op2.ors_tlimit = SLAP_NO_LIMIT;
369 	op2.ors_filter = (Filter *)slap_filter_objectClass_pres;
370 	op2.ors_filterstr = *slap_filterstr_objectClass_pres;
371 	op2.ors_attrs = slap_anlist_all_attributes;
372 	op2.ors_attrsonly = 0;
373 
374 	rc = op->o_bd->be_search( &op2, &rs2 );
375 	if ( rc != LDAP_SUCCESS ) {
376 		rc = rs->sr_err = btd.btd_rc;
377 		rs->sr_text = "subtree delete not possible";
378 		send_ldap_result( op, rs );
379 		goto clean;
380 	}
381 
382 	for ( ; btd.btd_eid != NULL;
383 		btd.btd_eid = backsql_free_entryID( btd.btd_eid,
384 			1, op->o_tmpmemctx ) )
385 	{
386 		Entry	*e = (void *)0xbad;
387 		rc = backsql_delete_int( op, rs, dbh, sthp, btd.btd_eid, &e );
388 		if ( rc != LDAP_SUCCESS ) {
389 			break;
390 		}
391 	}
392 
393 clean:;
394 	for ( ; btd.btd_eid != NULL;
395 		btd.btd_eid = backsql_free_entryID( btd.btd_eid,
396 			1, op->o_tmpmemctx ) )
397 		;
398 
399 	return rc;
400 }
401 
402 int
backsql_delete(Operation * op,SlapReply * rs)403 backsql_delete( Operation *op, SlapReply *rs )
404 {
405 	SQLHDBC 		dbh = SQL_NULL_HDBC;
406 	SQLHSTMT		sth = SQL_NULL_HSTMT;
407 	backsql_oc_map_rec	*oc = NULL;
408 	backsql_srch_info	bsi = { 0 };
409 	backsql_entryID		e_id = { 0 };
410 	Entry			d = { 0 }, p = { 0 }, *e = NULL;
411 	struct berval		pdn = BER_BVNULL;
412 	int			manageDSAit = get_manageDSAit( op );
413 
414 	Debug( LDAP_DEBUG_TRACE, "==>backsql_delete(): deleting entry \"%s\"\n",
415 			op->o_req_ndn.bv_val );
416 
417 	rs->sr_err = backsql_get_db_conn( op, &dbh );
418 	if ( rs->sr_err != LDAP_SUCCESS ) {
419 		Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
420 			"could not get connection handle - exiting\n" );
421 		rs->sr_text = ( rs->sr_err == LDAP_OTHER )
422 			? "SQL-backend error" : NULL;
423 		e = NULL;
424 		goto done;
425 	}
426 
427 	/*
428 	 * Get the entry
429 	 */
430 	bsi.bsi_e = &d;
431 	rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
432 			LDAP_SCOPE_BASE,
433 			(time_t)(-1), NULL, dbh, op, rs, slap_anlist_no_attrs,
434 			( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY | BACKSQL_ISF_GET_OC ) );
435 	switch ( rs->sr_err ) {
436 	case LDAP_SUCCESS:
437 		break;
438 
439 	case LDAP_REFERRAL:
440 		if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
441 				dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
442 		{
443 			rs->sr_err = LDAP_SUCCESS;
444 			rs->sr_text = NULL;
445 			rs->sr_matched = NULL;
446 			if ( rs->sr_ref ) {
447 				ber_bvarray_free( rs->sr_ref );
448 				rs->sr_ref = NULL;
449 			}
450 			break;
451 		}
452 		e = &d;
453 		/* fallthru */
454 
455 	default:
456 		Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
457 			"could not retrieve deleteDN ID - no such entry\n" );
458 		if ( !BER_BVISNULL( &d.e_nname ) ) {
459 			/* FIXME: should always be true! */
460 			e = &d;
461 
462 		} else {
463 			e = NULL;
464 		}
465 		goto done;
466 	}
467 
468 	if ( get_assert( op ) &&
469 			( test_filter( op, &d, get_assertion( op ) )
470 			  != LDAP_COMPARE_TRUE ) )
471 	{
472 		rs->sr_err = LDAP_ASSERTION_FAILED;
473 		e = &d;
474 		goto done;
475 	}
476 
477 	if ( !access_allowed( op, &d, slap_schema.si_ad_entry,
478 			NULL, ACL_WDEL, NULL ) )
479 	{
480 		Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
481 			"no write access to entry\n" );
482 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
483 		e = &d;
484 		goto done;
485 	}
486 
487 	rs->sr_err = backsql_has_children( op, dbh, &op->o_req_ndn );
488 	switch ( rs->sr_err ) {
489 	case LDAP_COMPARE_FALSE:
490 		rs->sr_err = LDAP_SUCCESS;
491 		break;
492 
493 	case LDAP_COMPARE_TRUE:
494 #ifdef SLAP_CONTROL_X_TREE_DELETE
495 		if ( get_treeDelete( op ) ) {
496 			rs->sr_err = LDAP_SUCCESS;
497 			break;
498 		}
499 #endif /* SLAP_CONTROL_X_TREE_DELETE */
500 
501 		Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
502 			"entry \"%s\" has children\n",
503 			op->o_req_dn.bv_val );
504 		rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
505 		rs->sr_text = "subordinate objects must be deleted first";
506 		/* fallthru */
507 
508 	default:
509 		e = &d;
510 		goto done;
511 	}
512 
513 	assert( bsi.bsi_base_id.eid_oc != NULL );
514 	oc = bsi.bsi_base_id.eid_oc;
515 	if ( oc->bom_delete_proc == NULL ) {
516 		Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
517 			"delete procedure is not defined "
518 			"for this objectclass - aborting\n" );
519 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
520 		rs->sr_text = "operation not permitted within namingContext";
521 		e = NULL;
522 		goto done;
523 	}
524 
525 	/*
526 	 * Get the parent
527 	 */
528 	e_id = bsi.bsi_base_id;
529 	memset( &bsi.bsi_base_id, 0, sizeof( bsi.bsi_base_id ) );
530 	if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
531 		dnParent( &op->o_req_ndn, &pdn );
532 		bsi.bsi_e = &p;
533 		rs->sr_err = backsql_init_search( &bsi, &pdn,
534 				LDAP_SCOPE_BASE,
535 				(time_t)(-1), NULL, dbh, op, rs,
536 				slap_anlist_no_attrs,
537 				BACKSQL_ISF_GET_ENTRY );
538 		if ( rs->sr_err != LDAP_SUCCESS ) {
539 			Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
540 				"could not retrieve deleteDN ID "
541 				"- no such entry\n" );
542 			e = &p;
543 			goto done;
544 		}
545 
546 		(void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
547 
548 		/* check parent for "children" acl */
549 		if ( !access_allowed( op, &p, slap_schema.si_ad_children,
550 				NULL, ACL_WDEL, NULL ) )
551 		{
552 			Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
553 				"no write access to parent\n" );
554 			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
555 			e = &p;
556 			goto done;
557 
558 		}
559 	}
560 
561 	e = &d;
562 #ifdef SLAP_CONTROL_X_TREE_DELETE
563 	if ( get_treeDelete( op ) ) {
564 		backsql_tree_delete( op, rs, dbh, &sth );
565 		if ( rs->sr_err == LDAP_OTHER || rs->sr_err == LDAP_SUCCESS )
566 		{
567 			e = NULL;
568 		}
569 
570 	} else
571 #endif /* SLAP_CONTROL_X_TREE_DELETE */
572 	{
573 		backsql_delete_int( op, rs, dbh, &sth, &e_id, &e );
574 	}
575 
576 	/*
577 	 * Commit only if all operations succeed
578 	 */
579 	if ( sth != SQL_NULL_HSTMT ) {
580 		SQLUSMALLINT	CompletionType = SQL_ROLLBACK;
581 
582 		if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
583 			assert( e == NULL );
584 			CompletionType = SQL_COMMIT;
585 		}
586 
587 		SQLTransact( SQL_NULL_HENV, dbh, CompletionType );
588 	}
589 
590 done:;
591 	if ( e != NULL ) {
592 		if ( !access_allowed( op, e, slap_schema.si_ad_entry, NULL,
593 					ACL_DISCLOSE, NULL ) )
594 		{
595 			rs->sr_err = LDAP_NO_SUCH_OBJECT;
596 			rs->sr_text = NULL;
597 			rs->sr_matched = NULL;
598 			if ( rs->sr_ref ) {
599 				ber_bvarray_free( rs->sr_ref );
600 				rs->sr_ref = NULL;
601 			}
602 		}
603 	}
604 
605 	if ( op->o_noop && rs->sr_err == LDAP_SUCCESS ) {
606 		rs->sr_err = LDAP_X_NO_OPERATION;
607 	}
608 
609 	send_ldap_result( op, rs );
610 
611 	Debug( LDAP_DEBUG_TRACE, "<==backsql_delete()\n" );
612 
613 	if ( !BER_BVISNULL( &e_id.eid_ndn ) ) {
614 		(void)backsql_free_entryID( &e_id, 0, op->o_tmpmemctx );
615 	}
616 
617 	if ( !BER_BVISNULL( &d.e_nname ) ) {
618 		backsql_entry_clean( op, &d );
619 	}
620 
621 	if ( !BER_BVISNULL( &p.e_nname ) ) {
622 		backsql_entry_clean( op, &p );
623 	}
624 
625 	if ( rs->sr_ref ) {
626 		ber_bvarray_free( rs->sr_ref );
627 		rs->sr_ref = NULL;
628 	}
629 
630 	return rs->sr_err;
631 }
632 
633