xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-sql/delete.c (revision 3816d47b2c42fcd6e549e3407f842a5b1a1d23ad)
1 /* $OpenLDAP: pkg/ldap/servers/slapd/back-sql/delete.c,v 1.35.2.8 2008/02/11 23:26:48 kurt Exp $ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2008 The OpenLDAP Foundation.
5  * Portions Copyright 1999 Dmitry Kovalev.
6  * Portions Copyright 2002 Pierangelo Masarati.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENTS:
18  * This work was initially developed by Dmitry Kovalev for inclusion
19  * by OpenLDAP Software.  Additional significant contributors include
20  * Pierangelo Masarati.
21  */
22 
23 #include "portable.h"
24 
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include "ac/string.h"
28 
29 #include "slap.h"
30 #include "proto-sql.h"
31 
32 typedef struct backsql_delete_attr_t {
33 	Operation 		*op;
34 	SlapReply		*rs;
35 	SQLHDBC			dbh;
36 	backsql_entryID		*e_id;
37 } backsql_delete_attr_t;
38 
39 static int
40 backsql_delete_attr_f( void *v_at, void *v_bda )
41 {
42 	backsql_at_map_rec	*at = (backsql_at_map_rec *)v_at;
43 	backsql_delete_attr_t	*bda = (backsql_delete_attr_t *)v_bda;
44 	int			rc;
45 
46 	rc = backsql_modify_delete_all_values( bda->op,
47 			bda->rs, bda->dbh, bda->e_id, at );
48 
49 	if ( rc != LDAP_SUCCESS ) {
50 		return BACKSQL_AVL_STOP;
51 	}
52 
53 	return BACKSQL_AVL_CONTINUE;
54 }
55 
56 static int
57 backsql_delete_all_attrs(
58 	Operation 		*op,
59 	SlapReply		*rs,
60 	SQLHDBC			dbh,
61 	backsql_entryID		*eid )
62 {
63 	backsql_delete_attr_t	bda;
64 	int			rc;
65 
66 	bda.op = op;
67 	bda.rs = rs;
68 	bda.dbh = dbh;
69 	bda.e_id = eid;
70 
71 	rc = avl_apply( eid->eid_oc->bom_attrs, backsql_delete_attr_f, &bda,
72 			BACKSQL_AVL_STOP, AVL_INORDER );
73 	if ( rc == BACKSQL_AVL_STOP ) {
74 		return rs->sr_err;
75 	}
76 
77 	return LDAP_SUCCESS;
78 }
79 
80 static int
81 backsql_delete_int(
82 	Operation	*op,
83 	SlapReply	*rs,
84 	SQLHDBC		dbh,
85 	SQLHSTMT	*sthp,
86 	backsql_entryID	*eid,
87 	Entry		**ep )
88 {
89 	backsql_info 		*bi = (backsql_info*)op->o_bd->be_private;
90 	SQLHSTMT		sth = SQL_NULL_HSTMT;
91 	RETCODE			rc;
92 	int			prc = LDAP_SUCCESS;
93 	/* first parameter no */
94 	SQLUSMALLINT		pno = 0;
95 
96 	sth = *sthp;
97 
98 	/* avl_apply ... */
99 	rs->sr_err = backsql_delete_all_attrs( op, rs, dbh, eid );
100 	if ( rs->sr_err != LDAP_SUCCESS ) {
101 		goto done;
102 	}
103 
104 	rc = backsql_Prepare( dbh, &sth, eid->eid_oc->bom_delete_proc, 0 );
105 	if ( rc != SQL_SUCCESS ) {
106 		Debug( LDAP_DEBUG_TRACE,
107 			"   backsql_delete(): "
108 			"error preparing delete query\n",
109 			0, 0, 0 );
110 		backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
111 
112 		rs->sr_err = LDAP_OTHER;
113 		rs->sr_text = "SQL-backend error";
114 		*ep = NULL;
115 		goto done;
116 	}
117 
118 	if ( BACKSQL_IS_DEL( eid->eid_oc->bom_expect_return ) ) {
119 		pno = 1;
120 		rc = backsql_BindParamInt( sth, 1, SQL_PARAM_OUTPUT, &prc );
121 		if ( rc != SQL_SUCCESS ) {
122 			Debug( LDAP_DEBUG_TRACE,
123 				"   backsql_delete(): "
124 				"error binding output parameter for objectClass %s\n",
125 				eid->eid_oc->bom_oc->soc_cname.bv_val, 0, 0 );
126 			backsql_PrintErrors( bi->sql_db_env, dbh,
127 				sth, rc );
128 			SQLFreeStmt( sth, SQL_DROP );
129 
130 			rs->sr_text = "SQL-backend error";
131 			rs->sr_err = LDAP_OTHER;
132 			*ep = NULL;
133 			goto done;
134 		}
135 	}
136 
137 	rc = backsql_BindParamID( sth, pno + 1, SQL_PARAM_INPUT, &eid->eid_keyval );
138 	if ( rc != SQL_SUCCESS ) {
139 		Debug( LDAP_DEBUG_TRACE,
140 			"   backsql_delete(): "
141 			"error binding keyval parameter for objectClass %s\n",
142 			eid->eid_oc->bom_oc->soc_cname.bv_val, 0, 0 );
143 		backsql_PrintErrors( bi->sql_db_env, dbh,
144 			sth, rc );
145 		SQLFreeStmt( sth, SQL_DROP );
146 
147 		rs->sr_text = "SQL-backend error";
148 		rs->sr_err = LDAP_OTHER;
149 		*ep = NULL;
150 		goto done;
151 	}
152 
153 	rc = SQLExecute( sth );
154 	if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
155 		rs->sr_err = LDAP_SUCCESS;
156 
157 	} else {
158 		Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
159 			"delete_proc execution failed (rc=%d, prc=%d)\n",
160 			rc, prc, 0 );
161 
162 
163 		if ( prc != LDAP_SUCCESS ) {
164 			/* SQL procedure executed fine
165 			 * but returned an error */
166 			rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
167 
168 		} else {
169 			backsql_PrintErrors( bi->sql_db_env, dbh,
170 					sth, rc );
171 			rs->sr_err = LDAP_OTHER;
172 		}
173 		SQLFreeStmt( sth, SQL_DROP );
174 		goto done;
175 	}
176 	SQLFreeStmt( sth, SQL_DROP );
177 
178 	/* delete "auxiliary" objectClasses, if any... */
179 	rc = backsql_Prepare( dbh, &sth, bi->sql_delobjclasses_stmt, 0 );
180 	if ( rc != SQL_SUCCESS ) {
181 		Debug( LDAP_DEBUG_TRACE,
182 			"   backsql_delete(): "
183 			"error preparing ldap_entry_objclasses delete query\n",
184 			0, 0, 0 );
185 		backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
186 
187 		rs->sr_err = LDAP_OTHER;
188 		rs->sr_text = "SQL-backend error";
189 		*ep = NULL;
190 		goto done;
191 	}
192 
193 	rc = backsql_BindParamID( sth, 1, SQL_PARAM_INPUT, &eid->eid_id );
194 	if ( rc != SQL_SUCCESS ) {
195 		Debug( LDAP_DEBUG_TRACE,
196 			"   backsql_delete(): "
197 			"error binding auxiliary objectClasses "
198 			"entry ID parameter for objectClass %s\n",
199 			eid->eid_oc->bom_oc->soc_cname.bv_val, 0, 0 );
200 		backsql_PrintErrors( bi->sql_db_env, dbh,
201 			sth, rc );
202 		SQLFreeStmt( sth, SQL_DROP );
203 
204 		rs->sr_text = "SQL-backend error";
205 		rs->sr_err = LDAP_OTHER;
206 		*ep = NULL;
207 		goto done;
208 	}
209 
210 	rc = SQLExecute( sth );
211 	switch ( rc ) {
212 	case SQL_NO_DATA:
213 		/* apparently there were no "auxiliary" objectClasses
214 		 * for this entry... */
215 	case SQL_SUCCESS:
216 		break;
217 
218 	default:
219 		Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
220 			"failed to delete record from ldap_entry_objclasses\n",
221 			0, 0, 0 );
222 		backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
223 		SQLFreeStmt( sth, SQL_DROP );
224 		rs->sr_err = LDAP_OTHER;
225 		rs->sr_text = "SQL-backend error";
226 		*ep = NULL;
227 		goto done;
228 	}
229 	SQLFreeStmt( sth, SQL_DROP );
230 
231 	/* delete entry... */
232 	rc = backsql_Prepare( dbh, &sth, bi->sql_delentry_stmt, 0 );
233 	if ( rc != SQL_SUCCESS ) {
234 		Debug( LDAP_DEBUG_TRACE,
235 			"   backsql_delete(): "
236 			"error preparing ldap_entries delete query\n",
237 			0, 0, 0 );
238 		backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
239 
240 		rs->sr_err = LDAP_OTHER;
241 		rs->sr_text = "SQL-backend error";
242 		*ep = NULL;
243 		goto done;
244 	}
245 
246 	rc = backsql_BindParamID( sth, 1, SQL_PARAM_INPUT, &eid->eid_id );
247 	if ( rc != SQL_SUCCESS ) {
248 		Debug( LDAP_DEBUG_TRACE,
249 			"   backsql_delete(): "
250 			"error binding entry ID parameter "
251 			"for objectClass %s\n",
252 			eid->eid_oc->bom_oc->soc_cname.bv_val, 0, 0 );
253 		backsql_PrintErrors( bi->sql_db_env, dbh,
254 			sth, rc );
255 		SQLFreeStmt( sth, SQL_DROP );
256 
257 		rs->sr_text = "SQL-backend error";
258 		rs->sr_err = LDAP_OTHER;
259 		*ep = NULL;
260 		goto done;
261 	}
262 
263 	rc = SQLExecute( sth );
264 	if ( rc != SQL_SUCCESS ) {
265 		Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
266 			"failed to delete record from ldap_entries\n",
267 			0, 0, 0 );
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
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
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 = { 0 };
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
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, 0, 0 );
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 			0, 0, 0 );
422 		rs->sr_text = ( rs->sr_err == LDAP_OTHER )
423 			? "SQL-backend error" : NULL;
424 		e = NULL;
425 		goto done;
426 	}
427 
428 	/*
429 	 * Get the entry
430 	 */
431 	bsi.bsi_e = &d;
432 	rs->sr_err = backsql_init_search( &bsi, &op->o_req_ndn,
433 			LDAP_SCOPE_BASE,
434 			(time_t)(-1), NULL, dbh, op, rs, slap_anlist_no_attrs,
435 			( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY | BACKSQL_ISF_GET_OC ) );
436 	switch ( rs->sr_err ) {
437 	case LDAP_SUCCESS:
438 		break;
439 
440 	case LDAP_REFERRAL:
441 		if ( manageDSAit && !BER_BVISNULL( &bsi.bsi_e->e_nname ) &&
442 				dn_match( &op->o_req_ndn, &bsi.bsi_e->e_nname ) )
443 		{
444 			rs->sr_err = LDAP_SUCCESS;
445 			rs->sr_text = NULL;
446 			rs->sr_matched = NULL;
447 			if ( rs->sr_ref ) {
448 				ber_bvarray_free( rs->sr_ref );
449 				rs->sr_ref = NULL;
450 			}
451 			break;
452 		}
453 		e = &d;
454 		/* fallthru */
455 
456 	default:
457 		Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
458 			"could not retrieve deleteDN ID - no such entry\n",
459 			0, 0, 0 );
460 		if ( !BER_BVISNULL( &d.e_nname ) ) {
461 			/* FIXME: should always be true! */
462 			e = &d;
463 
464 		} else {
465 			e = NULL;
466 		}
467 		goto done;
468 	}
469 
470 	if ( get_assert( op ) &&
471 			( test_filter( op, &d, get_assertion( op ) )
472 			  != LDAP_COMPARE_TRUE ) )
473 	{
474 		rs->sr_err = LDAP_ASSERTION_FAILED;
475 		e = &d;
476 		goto done;
477 	}
478 
479 	if ( !access_allowed( op, &d, slap_schema.si_ad_entry,
480 			NULL, ACL_WDEL, NULL ) )
481 	{
482 		Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
483 			"no write access to entry\n",
484 			0, 0, 0 );
485 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
486 		e = &d;
487 		goto done;
488 	}
489 
490 	rs->sr_err = backsql_has_children( op, dbh, &op->o_req_ndn );
491 	switch ( rs->sr_err ) {
492 	case LDAP_COMPARE_FALSE:
493 		rs->sr_err = LDAP_SUCCESS;
494 		break;
495 
496 	case LDAP_COMPARE_TRUE:
497 #ifdef SLAP_CONTROL_X_TREE_DELETE
498 		if ( get_treeDelete( op ) ) {
499 			rs->sr_err = LDAP_SUCCESS;
500 			break;
501 		}
502 #endif /* SLAP_CONTROL_X_TREE_DELETE */
503 
504 		Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
505 			"entry \"%s\" has children\n",
506 			op->o_req_dn.bv_val, 0, 0 );
507 		rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
508 		rs->sr_text = "subordinate objects must be deleted first";
509 		/* fallthru */
510 
511 	default:
512 		e = &d;
513 		goto done;
514 	}
515 
516 	assert( bsi.bsi_base_id.eid_oc != NULL );
517 	oc = bsi.bsi_base_id.eid_oc;
518 	if ( oc->bom_delete_proc == NULL ) {
519 		Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
520 			"delete procedure is not defined "
521 			"for this objectclass - aborting\n", 0, 0, 0 );
522 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
523 		rs->sr_text = "operation not permitted within namingContext";
524 		e = NULL;
525 		goto done;
526 	}
527 
528 	/*
529 	 * Get the parent
530 	 */
531 	e_id = bsi.bsi_base_id;
532 	memset( &bsi.bsi_base_id, 0, sizeof( bsi.bsi_base_id ) );
533 	if ( !be_issuffix( op->o_bd, &op->o_req_ndn ) ) {
534 		dnParent( &op->o_req_ndn, &pdn );
535 		bsi.bsi_e = &p;
536 		rs->sr_err = backsql_init_search( &bsi, &pdn,
537 				LDAP_SCOPE_BASE,
538 				(time_t)(-1), NULL, dbh, op, rs,
539 				slap_anlist_no_attrs,
540 				BACKSQL_ISF_GET_ENTRY );
541 		if ( rs->sr_err != LDAP_SUCCESS ) {
542 			Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
543 				"could not retrieve deleteDN ID "
544 				"- no such entry\n",
545 				0, 0, 0 );
546 			e = &p;
547 			goto done;
548 		}
549 
550 		(void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
551 
552 		/* check parent for "children" acl */
553 		if ( !access_allowed( op, &p, slap_schema.si_ad_children,
554 				NULL, ACL_WDEL, NULL ) )
555 		{
556 			Debug( LDAP_DEBUG_TRACE, "   backsql_delete(): "
557 				"no write access to parent\n",
558 				0, 0, 0 );
559 			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
560 			e = &p;
561 			goto done;
562 
563 		}
564 	}
565 
566 	e = &d;
567 #ifdef SLAP_CONTROL_X_TREE_DELETE
568 	if ( get_treeDelete( op ) ) {
569 		backsql_tree_delete( op, rs, dbh, &sth );
570 		if ( rs->sr_err == LDAP_OTHER || rs->sr_err == LDAP_SUCCESS )
571 		{
572 			e = NULL;
573 		}
574 
575 	} else
576 #endif /* SLAP_CONTROL_X_TREE_DELETE */
577 	{
578 		backsql_delete_int( op, rs, dbh, &sth, &e_id, &e );
579 	}
580 
581 	/*
582 	 * Commit only if all operations succeed
583 	 */
584 	if ( sth != SQL_NULL_HSTMT ) {
585 		SQLUSMALLINT	CompletionType = SQL_ROLLBACK;
586 
587 		if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
588 			assert( e == NULL );
589 			CompletionType = SQL_COMMIT;
590 		}
591 
592 		SQLTransact( SQL_NULL_HENV, dbh, CompletionType );
593 	}
594 
595 done:;
596 	if ( e != NULL ) {
597 		if ( !access_allowed( op, e, slap_schema.si_ad_entry, NULL,
598 					ACL_DISCLOSE, NULL ) )
599 		{
600 			rs->sr_err = LDAP_NO_SUCH_OBJECT;
601 			rs->sr_text = NULL;
602 			rs->sr_matched = NULL;
603 			if ( rs->sr_ref ) {
604 				ber_bvarray_free( rs->sr_ref );
605 				rs->sr_ref = NULL;
606 			}
607 		}
608 	}
609 
610 	if ( op->o_noop && rs->sr_err == LDAP_SUCCESS ) {
611 		rs->sr_err = LDAP_X_NO_OPERATION;
612 	}
613 
614 	send_ldap_result( op, rs );
615 
616 	Debug( LDAP_DEBUG_TRACE, "<==backsql_delete()\n", 0, 0, 0 );
617 
618 	if ( !BER_BVISNULL( &e_id.eid_ndn ) ) {
619 		(void)backsql_free_entryID( &e_id, 0, op->o_tmpmemctx );
620 	}
621 
622 	if ( !BER_BVISNULL( &d.e_nname ) ) {
623 		backsql_entry_clean( op, &d );
624 	}
625 
626 	if ( !BER_BVISNULL( &p.e_nname ) ) {
627 		backsql_entry_clean( op, &p );
628 	}
629 
630 	if ( rs->sr_ref ) {
631 		ber_bvarray_free( rs->sr_ref );
632 		rs->sr_ref = NULL;
633 	}
634 
635 	return rs->sr_err;
636 }
637 
638