xref: /netbsd-src/external/bsd/openldap/dist/servers/slapd/back-sql/add.c (revision 404fbe5fb94ca1e054339640cabb2801ce52dd30)
1 /* $OpenLDAP: pkg/ldap/servers/slapd/back-sql/add.c,v 1.50.2.6 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  * Portions Copyright 2004 Mark Adamson.
8  * All rights reserved.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted only as authorized by the OpenLDAP
12  * Public License.
13  *
14  * A copy of this license is available in the file LICENSE in the
15  * top-level directory of the distribution or, alternatively, at
16  * <http://www.OpenLDAP.org/license.html>.
17  */
18 /* ACKNOWLEDGEMENTS:
19  * This work was initially developed by Dmitry Kovalev for inclusion
20  * by OpenLDAP Software.  Additional significant contributors include
21  * Pierangelo Masarati and Mark Adamson.
22 
23  */
24 
25 #include "portable.h"
26 
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include "ac/string.h"
30 
31 #include "slap.h"
32 #include "proto-sql.h"
33 
34 #ifdef BACKSQL_SYNCPROV
35 #include <lutil.h>
36 #endif /* BACKSQL_SYNCPROV */
37 
38 /*
39  * Skip:
40  * - null values (e.g. delete modification)
41  * - single occurrence of objectClass, because it is already used
42  *   to determine how to build the SQL entry
43  * - operational attributes
44  * - empty attributes
45  */
46 #define backsql_opattr_skip(ad) \
47 	(is_at_operational( (ad)->ad_type ) && (ad) != slap_schema.si_ad_ref )
48 #define	backsql_attr_skip(ad, vals) \
49 	( \
50 		( (ad) == slap_schema.si_ad_objectClass \
51 				&& (vals) && BER_BVISNULL( &((vals)[ 1 ]) ) ) \
52 		|| backsql_opattr_skip( (ad) ) \
53 		|| ( (vals) && BER_BVISNULL( &((vals)[ 0 ]) ) ) \
54 	)
55 
56 int
57 backsql_modify_delete_all_values(
58 	Operation 		*op,
59 	SlapReply		*rs,
60 	SQLHDBC			dbh,
61 	backsql_entryID		*e_id,
62 	backsql_at_map_rec	*at )
63 {
64 	backsql_info	*bi = (backsql_info *)op->o_bd->be_private;
65 	RETCODE		rc;
66 	SQLHSTMT	asth = SQL_NULL_HSTMT;
67 	BACKSQL_ROW_NTS	row;
68 
69 	assert( at != NULL );
70 	if ( at->bam_delete_proc == NULL ) {
71 		Debug( LDAP_DEBUG_TRACE,
72 			"   backsql_modify_delete_all_values(): "
73 			"missing attribute value delete procedure "
74 			"for attr \"%s\"\n",
75 			at->bam_ad->ad_cname.bv_val, 0, 0 );
76 		if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
77 			rs->sr_text = "SQL-backend error";
78 			return rs->sr_err = LDAP_OTHER;
79 		}
80 
81 		return LDAP_SUCCESS;
82 	}
83 
84 	rc = backsql_Prepare( dbh, &asth, at->bam_query, 0 );
85 	if ( rc != SQL_SUCCESS ) {
86 		Debug( LDAP_DEBUG_TRACE,
87 			"   backsql_modify_delete_all_values(): "
88 			"error preparing attribute value select query "
89 			"\"%s\"\n",
90 			at->bam_query, 0, 0 );
91 		backsql_PrintErrors( bi->sql_db_env, dbh,
92 				asth, rc );
93 
94 		rs->sr_text = "SQL-backend error";
95 		return rs->sr_err = LDAP_OTHER;
96 	}
97 
98 	rc = backsql_BindParamID( asth, 1, SQL_PARAM_INPUT, &e_id->eid_keyval );
99 	if ( rc != SQL_SUCCESS ) {
100 		Debug( LDAP_DEBUG_TRACE,
101 			"   backsql_modify_delete_all_values(): "
102 			"error binding key value parameter "
103 			"to attribute value select query\n",
104 			0, 0, 0 );
105 		backsql_PrintErrors( bi->sql_db_env, dbh,
106 				asth, rc );
107 		SQLFreeStmt( asth, SQL_DROP );
108 
109 		rs->sr_text = "SQL-backend error";
110 		return rs->sr_err = LDAP_OTHER;
111 	}
112 
113 	rc = SQLExecute( asth );
114 	if ( !BACKSQL_SUCCESS( rc ) ) {
115 		Debug( LDAP_DEBUG_TRACE,
116 			"   backsql_modify_delete_all_values(): "
117 			"error executing attribute value select query\n",
118 			0, 0, 0 );
119 		backsql_PrintErrors( bi->sql_db_env, dbh,
120 				asth, rc );
121 		SQLFreeStmt( asth, SQL_DROP );
122 
123 		rs->sr_text = "SQL-backend error";
124 		return rs->sr_err = LDAP_OTHER;
125 	}
126 
127 	backsql_BindRowAsStrings_x( asth, &row, op->o_tmpmemctx );
128 	for ( rc = SQLFetch( asth );
129 			BACKSQL_SUCCESS( rc );
130 			rc = SQLFetch( asth ) )
131 	{
132 		int		i;
133 		/* first parameter no, parameter order */
134 		SQLUSMALLINT	pno = 0,
135 				po = 0;
136 		/* procedure return code */
137 		int		prc = LDAP_SUCCESS;
138 
139 		for ( i = 0; i < row.ncols; i++ ) {
140 			SQLHSTMT	sth = SQL_NULL_HSTMT;
141 			ber_len_t	col_len;
142 
143 			rc = backsql_Prepare( dbh, &sth, at->bam_delete_proc, 0 );
144 			if ( rc != SQL_SUCCESS ) {
145 				Debug( LDAP_DEBUG_TRACE,
146 					"   backsql_modify_delete_all_values(): "
147 					"error preparing attribute value "
148 					"delete procedure "
149 					"\"%s\"\n",
150 					at->bam_delete_proc, 0, 0 );
151 				backsql_PrintErrors( bi->sql_db_env, dbh,
152 						sth, rc );
153 
154 				rs->sr_text = "SQL-backend error";
155 				rs->sr_err = LDAP_OTHER;
156 				goto done;
157 			}
158 
159 	   		if ( BACKSQL_IS_DEL( at->bam_expect_return ) ) {
160 				pno = 1;
161 				rc = backsql_BindParamInt( sth, 1,
162 						SQL_PARAM_OUTPUT, &prc );
163 				if ( rc != SQL_SUCCESS ) {
164 					Debug( LDAP_DEBUG_TRACE,
165 						"   backsql_modify_delete_all_values(): "
166 						"error binding output parameter for %s[%d]\n",
167 						at->bam_ad->ad_cname.bv_val, i, 0 );
168 					backsql_PrintErrors( bi->sql_db_env, dbh,
169 						sth, rc );
170 					SQLFreeStmt( sth, SQL_DROP );
171 
172 					rs->sr_text = "SQL-backend error";
173 					rs->sr_err = LDAP_OTHER;
174 					goto done;
175 				}
176 			}
177 			po = ( BACKSQL_IS_DEL( at->bam_param_order ) ) > 0;
178 			rc = backsql_BindParamID( sth, pno + 1 + po,
179 				SQL_PARAM_INPUT, &e_id->eid_keyval );
180 			if ( rc != SQL_SUCCESS ) {
181 				Debug( LDAP_DEBUG_TRACE,
182 					"   backsql_modify_delete_all_values(): "
183 					"error binding keyval parameter for %s[%d]\n",
184 					at->bam_ad->ad_cname.bv_val, i, 0 );
185 				backsql_PrintErrors( bi->sql_db_env, dbh,
186 					sth, rc );
187 				SQLFreeStmt( sth, SQL_DROP );
188 
189 				rs->sr_text = "SQL-backend error";
190 				rs->sr_err = LDAP_OTHER;
191 				goto done;
192 			}
193 #ifdef BACKSQL_ARBITRARY_KEY
194 			Debug( LDAP_DEBUG_TRACE,
195 				"   backsql_modify_delete_all_values() "
196 				"arg(%d)=%s\n",
197 				pno + 1 + po, e_id->eid_keyval.bv_val, 0 );
198 #else /* ! BACKSQL_ARBITRARY_KEY */
199 			Debug( LDAP_DEBUG_TRACE,
200 				"   backsql_modify_delete_all_values() "
201 				"arg(%d)=%lu\n",
202 				pno + 1 + po, e_id->eid_keyval, 0 );
203 #endif /* ! BACKSQL_ARBITRARY_KEY */
204 
205 			/*
206 			 * check for syntax needed here
207 			 * maybe need binary bind?
208 			 */
209 			col_len = strlen( row.cols[ i ] );
210 			rc = backsql_BindParamStr( sth, pno + 2 - po,
211 				SQL_PARAM_INPUT, row.cols[ i ], col_len );
212 			if ( rc != SQL_SUCCESS ) {
213 				Debug( LDAP_DEBUG_TRACE,
214 					"   backsql_modify_delete_all_values(): "
215 					"error binding value parameter for %s[%d]\n",
216 					at->bam_ad->ad_cname.bv_val, i, 0 );
217 				backsql_PrintErrors( bi->sql_db_env, dbh,
218 					sth, rc );
219 				SQLFreeStmt( sth, SQL_DROP );
220 
221 				rs->sr_text = "SQL-backend error";
222 				rs->sr_err = LDAP_OTHER;
223 				goto done;
224 			}
225 
226 			Debug( LDAP_DEBUG_TRACE,
227 				"   backsql_modify_delete_all_values(): "
228 				"arg(%d)=%s; executing \"%s\"\n",
229 				pno + 2 - po, row.cols[ i ],
230 				at->bam_delete_proc );
231 			rc = SQLExecute( sth );
232 			if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
233 				rs->sr_err = LDAP_SUCCESS;
234 
235 			} else {
236 				Debug( LDAP_DEBUG_TRACE,
237 					"   backsql_modify_delete_all_values(): "
238 					"delete_proc "
239 					"execution failed (rc=%d, prc=%d)\n",
240 					rc, prc, 0 );
241 				if ( prc != LDAP_SUCCESS ) {
242 					/* SQL procedure executed fine
243 					 * but returned an error */
244 					rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
245 
246 				} else {
247 					backsql_PrintErrors( bi->sql_db_env, dbh,
248 							sth, rc );
249 					rs->sr_err = LDAP_OTHER;
250 				}
251 				rs->sr_text = op->o_req_dn.bv_val;
252 				SQLFreeStmt( sth, SQL_DROP );
253 				goto done;
254 			}
255 			SQLFreeStmt( sth, SQL_DROP );
256 		}
257 	}
258 
259 	rs->sr_err = LDAP_SUCCESS;
260 
261 done:;
262 	backsql_FreeRow_x( &row, op->o_tmpmemctx );
263 	SQLFreeStmt( asth, SQL_DROP );
264 
265 	return rs->sr_err;
266 }
267 
268 int
269 backsql_modify_internal(
270 	Operation 		*op,
271 	SlapReply		*rs,
272 	SQLHDBC			dbh,
273 	backsql_oc_map_rec	*oc,
274 	backsql_entryID		*e_id,
275 	Modifications		*modlist )
276 {
277 	backsql_info	*bi = (backsql_info *)op->o_bd->be_private;
278 	RETCODE		rc;
279 	Modifications	*ml;
280 
281 	Debug( LDAP_DEBUG_TRACE, "==>backsql_modify_internal(): "
282 		"traversing modifications list\n", 0, 0, 0 );
283 
284 	for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
285 		AttributeDescription	*ad;
286 		int			sm_op;
287 		static char		*sm_ops[] = { "add", "delete", "replace", "increment", NULL };
288 
289 		BerVarray		sm_values;
290 #if 0
291 		/* NOTE: some day we'll have to pass
292 		 * the normalized values as well */
293 		BerVarray		sm_nvalues;
294 #endif
295 		backsql_at_map_rec	*at = NULL;
296 		struct berval		*at_val;
297 		int			i;
298 
299 		ad = ml->sml_mod.sm_desc;
300 		sm_op = ( ml->sml_mod.sm_op & LDAP_MOD_OP );
301 		sm_values = ml->sml_mod.sm_values;
302 #if 0
303 		sm_nvalues = ml->sml_mod.sm_nvalues;
304 #endif
305 
306 		Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
307 			"modifying attribute \"%s\" (%s) according to "
308 			"mappings for objectClass \"%s\"\n",
309 			ad->ad_cname.bv_val, sm_ops[ sm_op ], BACKSQL_OC_NAME( oc ) );
310 
311 		if ( backsql_attr_skip( ad, sm_values ) ) {
312 			continue;
313 		}
314 
315   		at = backsql_ad2at( oc, ad );
316 		if ( at == NULL ) {
317 			Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
318 				"attribute \"%s\" is not registered "
319 				"in objectClass \"%s\"\n",
320 				ad->ad_cname.bv_val, BACKSQL_OC_NAME( oc ), 0 );
321 
322 			if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
323 				rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
324 				rs->sr_text = "operation not permitted "
325 					"within namingContext";
326 				goto done;
327 			}
328 
329 			continue;
330 		}
331 
332 		switch ( sm_op ) {
333 		case LDAP_MOD_REPLACE: {
334 			Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
335 				"replacing values for attribute \"%s\"\n",
336 				at->bam_ad->ad_cname.bv_val, 0, 0 );
337 
338 			if ( at->bam_add_proc == NULL ) {
339 				Debug( LDAP_DEBUG_TRACE,
340 					"   backsql_modify_internal(): "
341 					"add procedure is not defined "
342 					"for attribute \"%s\" "
343 					"- unable to perform replacements\n",
344 					at->bam_ad->ad_cname.bv_val, 0, 0 );
345 
346 				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
347 					rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
348 					rs->sr_text = "operation not permitted "
349 						"within namingContext";
350 					goto done;
351 				}
352 
353 				break;
354 			}
355 
356 			if ( at->bam_delete_proc == NULL ) {
357 				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
358 					Debug( LDAP_DEBUG_TRACE,
359 						"   backsql_modify_internal(): "
360 						"delete procedure is not defined "
361 						"for attribute \"%s\"\n",
362 						at->bam_ad->ad_cname.bv_val, 0, 0 );
363 
364 					rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
365 					rs->sr_text = "operation not permitted "
366 						"within namingContext";
367 					goto done;
368 				}
369 
370 				Debug( LDAP_DEBUG_TRACE,
371 					"   backsql_modify_internal(): "
372 					"delete procedure is not defined "
373 					"for attribute \"%s\" "
374 					"- adding only\n",
375 					at->bam_ad->ad_cname.bv_val, 0, 0 );
376 
377 				goto add_only;
378 			}
379 
380 del_all:
381 			rs->sr_err = backsql_modify_delete_all_values( op, rs, dbh, e_id, at );
382 			if ( rs->sr_err != LDAP_SUCCESS ) {
383 				goto done;
384 			}
385 
386 			/* LDAP_MOD_DELETE gets here if all values must be deleted */
387 			if ( sm_op == LDAP_MOD_DELETE ) {
388 				break;
389 			}
390 	       	}
391 
392 		/*
393 		 * PASSTHROUGH - to add new attributes -- do NOT add break
394 		 */
395 		case LDAP_MOD_ADD:
396 		/* case SLAP_MOD_SOFTADD: */
397 add_only:;
398 			if ( at->bam_add_proc == NULL ) {
399 				Debug( LDAP_DEBUG_TRACE,
400 					"   backsql_modify_internal(): "
401 					"add procedure is not defined "
402 					"for attribute \"%s\"\n",
403 					at->bam_ad->ad_cname.bv_val, 0, 0 );
404 
405 				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
406 					rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
407 					rs->sr_text = "operation not permitted "
408 						"within namingContext";
409 					goto done;
410 				}
411 
412 				break;
413 			}
414 
415 			Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
416 				"adding new values for attribute \"%s\"\n",
417 				at->bam_ad->ad_cname.bv_val, 0, 0 );
418 
419 			/* can't add a NULL val array */
420 			assert( sm_values != NULL );
421 
422 			for ( i = 0, at_val = sm_values;
423 					!BER_BVISNULL( at_val );
424 					i++, at_val++ )
425 			{
426 				SQLHSTMT	sth = SQL_NULL_HSTMT;
427 				/* first parameter position, parameter order */
428 				SQLUSMALLINT	pno = 0,
429 						po;
430 				/* procedure return code */
431 				int		prc = LDAP_SUCCESS;
432 
433 				rc = backsql_Prepare( dbh, &sth, at->bam_add_proc, 0 );
434 				if ( rc != SQL_SUCCESS ) {
435 					Debug( LDAP_DEBUG_TRACE,
436 						"   backsql_modify_internal(): "
437 						"error preparing add query\n",
438 						0, 0, 0 );
439 					backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
440 
441 					rs->sr_err = LDAP_OTHER;
442 					rs->sr_text = "SQL-backend error";
443 					goto done;
444 				}
445 
446 				if ( BACKSQL_IS_ADD( at->bam_expect_return ) ) {
447 					pno = 1;
448 	      				rc = backsql_BindParamInt( sth, 1,
449 						SQL_PARAM_OUTPUT, &prc );
450 					if ( rc != SQL_SUCCESS ) {
451 						Debug( LDAP_DEBUG_TRACE,
452 							"   backsql_modify_internal(): "
453 							"error binding output parameter for %s[%d]\n",
454 							at->bam_ad->ad_cname.bv_val, i, 0 );
455 						backsql_PrintErrors( bi->sql_db_env, dbh,
456 							sth, rc );
457 						SQLFreeStmt( sth, SQL_DROP );
458 
459 						rs->sr_text = "SQL-backend error";
460 						rs->sr_err = LDAP_OTHER;
461 						goto done;
462 					}
463 				}
464 				po = ( BACKSQL_IS_ADD( at->bam_param_order ) ) > 0;
465 				rc = backsql_BindParamID( sth, pno + 1 + po,
466 					SQL_PARAM_INPUT, &e_id->eid_keyval );
467 				if ( rc != SQL_SUCCESS ) {
468 					Debug( LDAP_DEBUG_TRACE,
469 						"   backsql_modify_internal(): "
470 						"error binding keyval parameter for %s[%d]\n",
471 						at->bam_ad->ad_cname.bv_val, i, 0 );
472 					backsql_PrintErrors( bi->sql_db_env, dbh,
473 						sth, rc );
474 					SQLFreeStmt( sth, SQL_DROP );
475 
476 					rs->sr_text = "SQL-backend error";
477 					rs->sr_err = LDAP_OTHER;
478 					goto done;
479 				}
480 #ifdef BACKSQL_ARBITRARY_KEY
481 				Debug( LDAP_DEBUG_TRACE,
482 					"   backsql_modify_internal(): "
483 					"arg(%d)=\"%s\"\n",
484 					pno + 1 + po, e_id->eid_keyval.bv_val, 0 );
485 #else /* ! BACKSQL_ARBITRARY_KEY */
486 				Debug( LDAP_DEBUG_TRACE,
487 					"   backsql_modify_internal(): "
488 					"arg(%d)=\"%lu\"\n",
489 					pno + 1 + po, e_id->eid_keyval, 0 );
490 #endif /* ! BACKSQL_ARBITRARY_KEY */
491 
492 				/*
493 				 * check for syntax needed here
494 				 * maybe need binary bind?
495 				 */
496 				rc = backsql_BindParamBerVal( sth, pno + 2 - po,
497 					SQL_PARAM_INPUT, at_val );
498 				if ( rc != SQL_SUCCESS ) {
499 					Debug( LDAP_DEBUG_TRACE,
500 						"   backsql_modify_internal(): "
501 						"error binding value parameter for %s[%d]\n",
502 						at->bam_ad->ad_cname.bv_val, i, 0 );
503 					backsql_PrintErrors( bi->sql_db_env, dbh,
504 						sth, rc );
505 					SQLFreeStmt( sth, SQL_DROP );
506 
507 					rs->sr_text = "SQL-backend error";
508 					rs->sr_err = LDAP_OTHER;
509 					goto done;
510 				}
511 				Debug( LDAP_DEBUG_TRACE,
512 					"   backsql_modify_internal(): "
513 					"arg(%d)=\"%s\"; executing \"%s\"\n",
514 					pno + 2 - po, at_val->bv_val,
515 					at->bam_add_proc );
516 
517 				rc = SQLExecute( sth );
518 				if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
519 					rs->sr_err = LDAP_SUCCESS;
520 
521 				} else {
522 					Debug( LDAP_DEBUG_TRACE,
523 						"   backsql_modify_internal(): "
524 						"add_proc execution failed "
525 						"(rc=%d, prc=%d)\n",
526 						rc, prc, 0 );
527 					if ( prc != LDAP_SUCCESS ) {
528 						/* SQL procedure executed fine
529 						 * but returned an error */
530 						SQLFreeStmt( sth, SQL_DROP );
531 
532 						rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
533 						rs->sr_text = at->bam_ad->ad_cname.bv_val;
534 						return rs->sr_err;
535 
536 					} else {
537 						backsql_PrintErrors( bi->sql_db_env, dbh,
538 								sth, rc );
539 						if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) )
540 						{
541 							SQLFreeStmt( sth, SQL_DROP );
542 
543 							rs->sr_err = LDAP_OTHER;
544 							rs->sr_text = "SQL-backend error";
545 							goto done;
546 						}
547 					}
548 				}
549 				SQLFreeStmt( sth, SQL_DROP );
550 			}
551 			break;
552 
553 	      	case LDAP_MOD_DELETE:
554 			if ( at->bam_delete_proc == NULL ) {
555 				Debug( LDAP_DEBUG_TRACE,
556 					"   backsql_modify_internal(): "
557 					"delete procedure is not defined "
558 					"for attribute \"%s\"\n",
559 					at->bam_ad->ad_cname.bv_val, 0, 0 );
560 
561 				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
562 					rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
563 					rs->sr_text = "operation not permitted "
564 						"within namingContext";
565 					goto done;
566 				}
567 
568 				break;
569 			}
570 
571 			if ( sm_values == NULL ) {
572 				Debug( LDAP_DEBUG_TRACE,
573 					"   backsql_modify_internal(): "
574 					"no values given to delete "
575 					"for attribute \"%s\" "
576 					"-- deleting all values\n",
577 					at->bam_ad->ad_cname.bv_val, 0, 0 );
578 				goto del_all;
579 			}
580 
581 			Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
582 				"deleting values for attribute \"%s\"\n",
583 				at->bam_ad->ad_cname.bv_val, 0, 0 );
584 
585 			for ( i = 0, at_val = sm_values;
586 					!BER_BVISNULL( at_val );
587 					i++, at_val++ )
588 			{
589 				SQLHSTMT	sth = SQL_NULL_HSTMT;
590 				/* first parameter position, parameter order */
591 				SQLUSMALLINT	pno = 0,
592 						po;
593 				/* procedure return code */
594 				int		prc = LDAP_SUCCESS;
595 
596 				rc = backsql_Prepare( dbh, &sth, at->bam_delete_proc, 0 );
597 				if ( rc != SQL_SUCCESS ) {
598 					Debug( LDAP_DEBUG_TRACE,
599 						"   backsql_modify_internal(): "
600 						"error preparing delete query\n",
601 						0, 0, 0 );
602 					backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
603 
604 					rs->sr_err = LDAP_OTHER;
605 					rs->sr_text = "SQL-backend error";
606 					goto done;
607 				}
608 
609 				if ( BACKSQL_IS_DEL( at->bam_expect_return ) ) {
610 					pno = 1;
611 					rc = backsql_BindParamInt( sth, 1,
612 						SQL_PARAM_OUTPUT, &prc );
613 					if ( rc != SQL_SUCCESS ) {
614 						Debug( LDAP_DEBUG_TRACE,
615 							"   backsql_modify_internal(): "
616 							"error binding output parameter for %s[%d]\n",
617 							at->bam_ad->ad_cname.bv_val, i, 0 );
618 						backsql_PrintErrors( bi->sql_db_env, dbh,
619 							sth, rc );
620 						SQLFreeStmt( sth, SQL_DROP );
621 
622 						rs->sr_text = "SQL-backend error";
623 						rs->sr_err = LDAP_OTHER;
624 						goto done;
625 					}
626 				}
627 				po = ( BACKSQL_IS_DEL( at->bam_param_order ) ) > 0;
628 				rc = backsql_BindParamID( sth, pno + 1 + po,
629 					SQL_PARAM_INPUT, &e_id->eid_keyval );
630 				if ( rc != SQL_SUCCESS ) {
631 					Debug( LDAP_DEBUG_TRACE,
632 						"   backsql_modify_internal(): "
633 						"error binding keyval parameter for %s[%d]\n",
634 						at->bam_ad->ad_cname.bv_val, i, 0 );
635 					backsql_PrintErrors( bi->sql_db_env, dbh,
636 						sth, rc );
637 					SQLFreeStmt( sth, SQL_DROP );
638 
639 					rs->sr_text = "SQL-backend error";
640 					rs->sr_err = LDAP_OTHER;
641 					goto done;
642 				}
643 #ifdef BACKSQL_ARBITRARY_KEY
644 				Debug( LDAP_DEBUG_TRACE,
645 					"   backsql_modify_internal(): "
646 					"arg(%d)=\"%s\"\n",
647 					pno + 1 + po, e_id->eid_keyval.bv_val, 0 );
648 #else /* ! BACKSQL_ARBITRARY_KEY */
649 				Debug( LDAP_DEBUG_TRACE,
650 					"   backsql_modify_internal(): "
651 					"arg(%d)=\"%lu\"\n",
652 					pno + 1 + po, e_id->eid_keyval, 0 );
653 #endif /* ! BACKSQL_ARBITRARY_KEY */
654 
655 				/*
656 				 * check for syntax needed here
657 				 * maybe need binary bind?
658 				 */
659 				rc = backsql_BindParamBerVal( sth, pno + 2 - po,
660 					SQL_PARAM_INPUT, at_val );
661 				if ( rc != SQL_SUCCESS ) {
662 					Debug( LDAP_DEBUG_TRACE,
663 						"   backsql_modify_internal(): "
664 						"error binding value parameter for %s[%d]\n",
665 						at->bam_ad->ad_cname.bv_val, i, 0 );
666 					backsql_PrintErrors( bi->sql_db_env, dbh,
667 						sth, rc );
668 					SQLFreeStmt( sth, SQL_DROP );
669 
670 					rs->sr_text = "SQL-backend error";
671 					rs->sr_err = LDAP_OTHER;
672 					goto done;
673 				}
674 
675 				Debug( LDAP_DEBUG_TRACE,
676 					"   backsql_modify_internal(): "
677 					"executing \"%s\"\n",
678 					at->bam_delete_proc, 0, 0 );
679 				rc = SQLExecute( sth );
680 				if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS )
681 				{
682 					rs->sr_err = LDAP_SUCCESS;
683 
684 				} else {
685 					Debug( LDAP_DEBUG_TRACE,
686 						"   backsql_modify_internal(): "
687 						"delete_proc execution "
688 						"failed (rc=%d, prc=%d)\n",
689 						rc, prc, 0 );
690 
691 					if ( prc != LDAP_SUCCESS ) {
692 						/* SQL procedure executed fine
693 						 * but returned an error */
694 						rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
695 						rs->sr_text = at->bam_ad->ad_cname.bv_val;
696 						goto done;
697 
698 					} else {
699 						backsql_PrintErrors( bi->sql_db_env,
700 								dbh, sth, rc );
701 						SQLFreeStmt( sth, SQL_DROP );
702 						rs->sr_err = LDAP_OTHER;
703 						rs->sr_text = at->bam_ad->ad_cname.bv_val;
704 						goto done;
705 					}
706 				}
707 				SQLFreeStmt( sth, SQL_DROP );
708 			}
709 			break;
710 
711 	      	case LDAP_MOD_INCREMENT:
712 			Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
713 				"increment not supported yet\n", 0, 0, 0 );
714 			if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
715 				rs->sr_err = LDAP_OTHER;
716 				rs->sr_text = "SQL-backend error";
717 				goto done;
718 			}
719 			break;
720 		}
721 	}
722 
723 done:;
724 	Debug( LDAP_DEBUG_TRACE, "<==backsql_modify_internal(): %d%s%s\n",
725 		rs->sr_err,
726 		rs->sr_text ? ": " : "",
727 		rs->sr_text ? rs->sr_text : "" );
728 
729 	/*
730 	 * FIXME: should fail in case one change fails?
731 	 */
732 	return rs->sr_err;
733 }
734 
735 static int
736 backsql_add_attr(
737 	Operation		*op,
738 	SlapReply		*rs,
739 	SQLHDBC 		dbh,
740 	backsql_oc_map_rec 	*oc,
741 	Attribute		*at,
742 	unsigned long		new_keyval )
743 {
744 	backsql_info		*bi = (backsql_info*)op->o_bd->be_private;
745 	backsql_at_map_rec	*at_rec = NULL;
746 	struct berval		*at_val;
747 	unsigned long		i;
748 	RETCODE			rc;
749 	SQLUSMALLINT		currpos;
750 	SQLHSTMT 		sth = SQL_NULL_HSTMT;
751 
752 	at_rec = backsql_ad2at( oc, at->a_desc );
753 
754 	if ( at_rec == NULL ) {
755 		Debug( LDAP_DEBUG_TRACE, "   backsql_add_attr(\"%s\"): "
756 			"attribute \"%s\" is not registered "
757 			"in objectclass \"%s\"\n",
758 			op->ora_e->e_name.bv_val,
759 			at->a_desc->ad_cname.bv_val,
760 			BACKSQL_OC_NAME( oc ) );
761 
762 		if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
763 			rs->sr_text = "operation not permitted "
764 				"within namingContext";
765 			return rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
766 		}
767 
768 		return LDAP_SUCCESS;
769 	}
770 
771 	if ( at_rec->bam_add_proc == NULL ) {
772 		Debug( LDAP_DEBUG_TRACE, "   backsql_add_attr(\"%s\"): "
773 			"add procedure is not defined "
774 			"for attribute \"%s\" "
775 			"of structuralObjectClass \"%s\"\n",
776 			op->ora_e->e_name.bv_val,
777 			at->a_desc->ad_cname.bv_val,
778 			BACKSQL_OC_NAME( oc ) );
779 
780 		if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
781 			rs->sr_text = "operation not permitted "
782 				"within namingContext";
783 			return rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
784 		}
785 
786 		return LDAP_SUCCESS;
787 	}
788 
789 	for ( i = 0, at_val = &at->a_vals[ i ];
790 		       	!BER_BVISNULL( at_val );
791 			i++, at_val = &at->a_vals[ i ] )
792 	{
793 		/* procedure return code */
794 		int		prc = LDAP_SUCCESS;
795 		/* first parameter #, parameter order */
796 		SQLUSMALLINT	pno, po;
797 		char		logbuf[ STRLENOF("val[], id=") + 2*LDAP_PVT_INTTYPE_CHARS(unsigned long)];
798 
799 		/*
800 		 * Do not deal with the objectClass that is used
801 		 * to build the entry
802 		 */
803 		if ( at->a_desc == slap_schema.si_ad_objectClass ) {
804 			if ( dn_match( at_val, &oc->bom_oc->soc_cname ) )
805 			{
806 				continue;
807 			}
808 		}
809 
810 		rc = backsql_Prepare( dbh, &sth, at_rec->bam_add_proc, 0 );
811 		if ( rc != SQL_SUCCESS ) {
812 			rs->sr_text = "SQL-backend error";
813 			return rs->sr_err = LDAP_OTHER;
814 		}
815 
816 		if ( BACKSQL_IS_ADD( at_rec->bam_expect_return ) ) {
817 			pno = 1;
818 			rc = backsql_BindParamInt( sth, 1, SQL_PARAM_OUTPUT, &prc );
819 			if ( rc != SQL_SUCCESS ) {
820 				Debug( LDAP_DEBUG_TRACE,
821 					"   backsql_add_attr(): "
822 					"error binding output parameter for %s[%lu]\n",
823 					at_rec->bam_ad->ad_cname.bv_val, i, 0 );
824 				backsql_PrintErrors( bi->sql_db_env, dbh,
825 					sth, rc );
826 				SQLFreeStmt( sth, SQL_DROP );
827 
828 				rs->sr_text = "SQL-backend error";
829 				return rs->sr_err = LDAP_OTHER;
830 			}
831 
832 		} else {
833 			pno = 0;
834 		}
835 
836 		po = ( BACKSQL_IS_ADD( at_rec->bam_param_order ) ) > 0;
837 		currpos = pno + 1 + po;
838 		rc = backsql_BindParamInt( sth, currpos,
839 				SQL_PARAM_INPUT, &new_keyval );
840 		if ( rc != SQL_SUCCESS ) {
841 			Debug( LDAP_DEBUG_TRACE,
842 				"   backsql_add_attr(): "
843 				"error binding keyval parameter for %s[%lu]\n",
844 				at_rec->bam_ad->ad_cname.bv_val, i, 0 );
845 			backsql_PrintErrors( bi->sql_db_env, dbh,
846 				sth, rc );
847 			SQLFreeStmt( sth, SQL_DROP );
848 
849 			rs->sr_text = "SQL-backend error";
850 			return rs->sr_err = LDAP_OTHER;
851 		}
852 
853 		currpos = pno + 2 - po;
854 
855 		/*
856 		 * check for syntax needed here
857 		 * maybe need binary bind?
858 		 */
859 
860 		rc = backsql_BindParamBerVal( sth, currpos, SQL_PARAM_INPUT, at_val );
861 		if ( rc != SQL_SUCCESS ) {
862 			Debug( LDAP_DEBUG_TRACE,
863 				"   backsql_add_attr(): "
864 				"error binding value parameter for %s[%lu]\n",
865 				at_rec->bam_ad->ad_cname.bv_val, i, 0 );
866 			backsql_PrintErrors( bi->sql_db_env, dbh,
867 				sth, rc );
868 			SQLFreeStmt( sth, SQL_DROP );
869 
870 			rs->sr_text = "SQL-backend error";
871 			return rs->sr_err = LDAP_OTHER;
872 		}
873 
874 #ifdef LDAP_DEBUG
875 		snprintf( logbuf, sizeof( logbuf ), "val[%lu], id=%lu",
876 				i, new_keyval );
877 		Debug( LDAP_DEBUG_TRACE, "   backsql_add_attr(\"%s\"): "
878 			"executing \"%s\" %s\n",
879 			op->ora_e->e_name.bv_val,
880 			at_rec->bam_add_proc, logbuf );
881 #endif
882 		rc = SQLExecute( sth );
883 		if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
884 			rs->sr_err = LDAP_SUCCESS;
885 
886 		} else {
887 			Debug( LDAP_DEBUG_TRACE,
888 				"   backsql_add_attr(\"%s\"): "
889 				"add_proc execution failed (rc=%d, prc=%d)\n",
890 				op->ora_e->e_name.bv_val, rc, prc );
891 			if ( prc != LDAP_SUCCESS ) {
892 				/* SQL procedure executed fine
893 				 * but returned an error */
894 				rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
895 				rs->sr_text = op->ora_e->e_name.bv_val;
896 				SQLFreeStmt( sth, SQL_DROP );
897 				return rs->sr_err;
898 
899 			} else {
900 				backsql_PrintErrors( bi->sql_db_env, dbh,
901 						sth, rc );
902 				rs->sr_err = LDAP_OTHER;
903 				rs->sr_text = op->ora_e->e_name.bv_val;
904 				SQLFreeStmt( sth, SQL_DROP );
905 				return rs->sr_err;
906 			}
907 		}
908 		SQLFreeStmt( sth, SQL_DROP );
909 	}
910 
911 	return LDAP_SUCCESS;
912 }
913 
914 int
915 backsql_add( Operation *op, SlapReply *rs )
916 {
917 	backsql_info		*bi = (backsql_info*)op->o_bd->be_private;
918 	SQLHDBC 		dbh = SQL_NULL_HDBC;
919 	SQLHSTMT 		sth = SQL_NULL_HSTMT;
920 	unsigned long		new_keyval = 0;
921 	RETCODE			rc;
922 	backsql_oc_map_rec 	*oc = NULL;
923 	backsql_srch_info	bsi = { 0 };
924 	Entry			p = { 0 }, *e = NULL;
925 	Attribute		*at,
926 				*at_objectClass = NULL;
927 	ObjectClass		*soc = NULL;
928 	struct berval		scname = BER_BVNULL;
929 	struct berval		pdn;
930 	struct berval		realdn = BER_BVNULL;
931 	int			colnum;
932 	slap_mask_t		mask;
933 
934 	char			textbuf[ SLAP_TEXT_BUFLEN ];
935 	size_t			textlen = sizeof( textbuf );
936 
937 #ifdef BACKSQL_SYNCPROV
938 	/*
939 	 * NOTE: fake successful result to force contextCSN to be bumped up
940 	 */
941 	if ( op->o_sync ) {
942 		char		buf[ LDAP_LUTIL_CSNSTR_BUFSIZE ];
943 		struct berval	csn;
944 
945 		csn.bv_val = buf;
946 		csn.bv_len = sizeof( buf );
947 		slap_get_csn( op, &csn, 1 );
948 
949 		rs->sr_err = LDAP_SUCCESS;
950 		send_ldap_result( op, rs );
951 
952 		slap_graduate_commit_csn( op );
953 
954 		return 0;
955 	}
956 #endif /* BACKSQL_SYNCPROV */
957 
958 	Debug( LDAP_DEBUG_TRACE, "==>backsql_add(\"%s\")\n",
959 			op->ora_e->e_name.bv_val, 0, 0 );
960 
961 	/* check schema */
962 	if ( BACKSQL_CHECK_SCHEMA( bi ) ) {
963 		char		textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };
964 
965 		rs->sr_err = entry_schema_check( op, op->ora_e, NULL, 0, 1,
966 			&rs->sr_text, textbuf, sizeof( textbuf ) );
967 		if ( rs->sr_err != LDAP_SUCCESS ) {
968 			Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
969 				"entry failed schema check -- aborting\n",
970 				op->ora_e->e_name.bv_val, 0, 0 );
971 			e = NULL;
972 			goto done;
973 		}
974 	}
975 
976 	slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
977 
978 	/* search structuralObjectClass */
979 	for ( at = op->ora_e->e_attrs; at != NULL; at = at->a_next ) {
980 		if ( at->a_desc == slap_schema.si_ad_structuralObjectClass ) {
981 			break;
982 		}
983 	}
984 
985 	/* there must exist */
986 	if ( at == NULL ) {
987 		char		buf[ SLAP_TEXT_BUFLEN ];
988 		const char	*text;
989 
990 		/* search structuralObjectClass */
991 		for ( at = op->ora_e->e_attrs; at != NULL; at = at->a_next ) {
992 			if ( at->a_desc == slap_schema.si_ad_objectClass ) {
993 				break;
994 			}
995 		}
996 
997 		if ( at == NULL ) {
998 			Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
999 				"no objectClass\n",
1000 				op->ora_e->e_name.bv_val, 0, 0 );
1001 			rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
1002 			e = NULL;
1003 			goto done;
1004 		}
1005 
1006 		rs->sr_err = structural_class( at->a_vals, &soc, NULL,
1007 				&text, buf, sizeof( buf ), op->o_tmpmemctx );
1008 		if ( rs->sr_err != LDAP_SUCCESS ) {
1009 			Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1010 				"%s (%d)\n",
1011 				op->ora_e->e_name.bv_val, text, rs->sr_err );
1012 			e = NULL;
1013 			goto done;
1014 		}
1015 		scname = soc->soc_cname;
1016 
1017 	} else {
1018 		scname = at->a_vals[0];
1019 	}
1020 
1021 	/* I guess we should play with sub/supertypes to find a suitable oc */
1022 	oc = backsql_name2oc( bi, &scname );
1023 
1024 	if ( oc == NULL ) {
1025 		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1026 			"cannot map structuralObjectClass \"%s\" -- aborting\n",
1027 			op->ora_e->e_name.bv_val,
1028 			scname.bv_val, 0 );
1029 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1030 		rs->sr_text = "operation not permitted within namingContext";
1031 		e = NULL;
1032 		goto done;
1033 	}
1034 
1035 	if ( oc->bom_create_proc == NULL ) {
1036 		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1037 			"create procedure is not defined "
1038 			"for structuralObjectClass \"%s\" - aborting\n",
1039 			op->ora_e->e_name.bv_val,
1040 			scname.bv_val, 0 );
1041 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1042 		rs->sr_text = "operation not permitted within namingContext";
1043 		e = NULL;
1044 		goto done;
1045 
1046 	} else if ( BACKSQL_CREATE_NEEDS_SELECT( bi )
1047 			&& oc->bom_create_keyval == NULL ) {
1048 		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1049 			"create procedure needs select procedure, "
1050 			"but none is defined for structuralObjectClass \"%s\" "
1051 			"- aborting\n",
1052 			op->ora_e->e_name.bv_val,
1053 			scname.bv_val, 0 );
1054 		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1055 		rs->sr_text = "operation not permitted within namingContext";
1056 		e = NULL;
1057 		goto done;
1058 	}
1059 
1060 	/* check write access */
1061 	if ( !access_allowed_mask( op, op->ora_e,
1062 				slap_schema.si_ad_entry,
1063 				NULL, ACL_WADD, NULL, &mask ) )
1064 	{
1065 		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1066 		e = op->ora_e;
1067 		goto done;
1068 	}
1069 
1070 	rs->sr_err = backsql_get_db_conn( op, &dbh );
1071 	if ( rs->sr_err != LDAP_SUCCESS ) {
1072 		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1073 			"could not get connection handle - exiting\n",
1074 			op->ora_e->e_name.bv_val, 0, 0 );
1075 		rs->sr_text = ( rs->sr_err == LDAP_OTHER )
1076 			?  "SQL-backend error" : NULL;
1077 		e = NULL;
1078 		goto done;
1079 	}
1080 
1081 	/*
1082 	 * Check if entry exists
1083 	 *
1084 	 * NOTE: backsql_api_dn2odbc() is called explicitly because
1085 	 * we need the mucked DN to pass it to the create procedure.
1086 	 */
1087 	realdn = op->ora_e->e_name;
1088 	if ( backsql_api_dn2odbc( op, rs, &realdn ) ) {
1089 		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1090 			"backsql_api_dn2odbc(\"%s\") failed\n",
1091 			op->ora_e->e_name.bv_val, realdn.bv_val, 0 );
1092 		rs->sr_err = LDAP_OTHER;
1093 		rs->sr_text = "SQL-backend error";
1094 		e = NULL;
1095 		goto done;
1096 	}
1097 
1098 	rs->sr_err = backsql_dn2id( op, rs, dbh, &realdn, NULL, 0, 0 );
1099 	if ( rs->sr_err == LDAP_SUCCESS ) {
1100 		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1101 			"entry exists\n",
1102 			op->ora_e->e_name.bv_val, 0, 0 );
1103 		rs->sr_err = LDAP_ALREADY_EXISTS;
1104 		e = op->ora_e;
1105 		goto done;
1106 	}
1107 
1108 	/*
1109 	 * Get the parent dn and see if the corresponding entry exists.
1110 	 */
1111 	if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
1112 		pdn = slap_empty_bv;
1113 
1114 	} else {
1115 		dnParent( &op->ora_e->e_nname, &pdn );
1116 
1117 		/*
1118 		 * Get the parent
1119 		 */
1120 		bsi.bsi_e = &p;
1121 		rs->sr_err = backsql_init_search( &bsi, &pdn,
1122 				LDAP_SCOPE_BASE,
1123 				(time_t)(-1), NULL, dbh, op, rs, slap_anlist_no_attrs,
1124 				( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
1125 		if ( rs->sr_err != LDAP_SUCCESS ) {
1126 			Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1127 				"could not retrieve addDN parent "
1128 				"\"%s\" ID - %s matched=\"%s\"\n",
1129 				pdn.bv_val,
1130 				rs->sr_err == LDAP_REFERRAL ? "referral" : "no such entry",
1131 				rs->sr_matched ? rs->sr_matched : "(null)" );
1132 			e = &p;
1133 			goto done;
1134 		}
1135 
1136 		/* check "children" pseudo-attribute access to parent */
1137 		if ( !access_allowed( op, &p, slap_schema.si_ad_children,
1138 					NULL, ACL_WADD, NULL ) )
1139 		{
1140 			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1141 			e = &p;
1142 			goto done;
1143 		}
1144 	}
1145 
1146 	/*
1147 	 * create_proc is executed; if expect_return is set, then
1148 	 * an output parameter is bound, which should contain
1149 	 * the id of the added row; otherwise the procedure
1150 	 * is expected to return the id as the first column of a select
1151 	 */
1152 	rc = backsql_Prepare( dbh, &sth, oc->bom_create_proc, 0 );
1153 	if ( rc != SQL_SUCCESS ) {
1154 		rs->sr_err = LDAP_OTHER;
1155 		rs->sr_text = "SQL-backend error";
1156 		e = NULL;
1157 		goto done;
1158 	}
1159 
1160 	colnum = 1;
1161 	if ( BACKSQL_IS_ADD( oc->bom_expect_return ) ) {
1162 		rc = backsql_BindParamInt( sth, 1, SQL_PARAM_OUTPUT, &new_keyval );
1163 		if ( rc != SQL_SUCCESS ) {
1164 			Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1165 				"error binding keyval parameter "
1166 				"for objectClass %s\n",
1167 				op->ora_e->e_name.bv_val,
1168 				oc->bom_oc->soc_cname.bv_val, 0 );
1169 			backsql_PrintErrors( bi->sql_db_env, dbh,
1170 				sth, rc );
1171 			SQLFreeStmt( sth, SQL_DROP );
1172 
1173 			rs->sr_text = "SQL-backend error";
1174 			rs->sr_err = LDAP_OTHER;
1175 			e = NULL;
1176 			goto done;
1177 		}
1178 		colnum++;
1179 	}
1180 
1181 	if ( oc->bom_create_hint ) {
1182 		at = attr_find( op->ora_e->e_attrs, oc->bom_create_hint );
1183 		if ( at && at->a_vals ) {
1184 			backsql_BindParamStr( sth, colnum, SQL_PARAM_INPUT,
1185 					at->a_vals[0].bv_val,
1186 					at->a_vals[0].bv_len );
1187 			Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1188 					"create_proc hint: param = '%s'\n",
1189 					at->a_vals[0].bv_val, 0, 0 );
1190 
1191 		} else {
1192 			backsql_BindParamStr( sth, colnum, SQL_PARAM_INPUT,
1193 					"", 0 );
1194 			Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1195 					"create_proc hint (%s) not avalable\n",
1196 					oc->bom_create_hint->ad_cname.bv_val,
1197 					0, 0 );
1198 		}
1199 		colnum++;
1200 	}
1201 
1202 	Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): executing \"%s\"\n",
1203 		op->ora_e->e_name.bv_val, oc->bom_create_proc, 0 );
1204 	rc = SQLExecute( sth );
1205 	if ( rc != SQL_SUCCESS ) {
1206 		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1207 			"create_proc execution failed\n",
1208 			op->ora_e->e_name.bv_val, 0, 0 );
1209 		backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc);
1210 		SQLFreeStmt( sth, SQL_DROP );
1211 		rs->sr_err = LDAP_OTHER;
1212 		rs->sr_text = "SQL-backend error";
1213 		e = NULL;
1214 		goto done;
1215 	}
1216 
1217 	/* FIXME: after SQLExecute(), the row is already inserted
1218 	 * (at least with PostgreSQL and unixODBC); needs investigation */
1219 
1220 	if ( !BACKSQL_IS_ADD( oc->bom_expect_return ) ) {
1221 		SWORD		ncols;
1222 		SQLINTEGER	value_len;
1223 
1224 		if ( BACKSQL_CREATE_NEEDS_SELECT( bi ) ) {
1225 			SQLFreeStmt( sth, SQL_DROP );
1226 
1227 			rc = backsql_Prepare( dbh, &sth, oc->bom_create_keyval, 0 );
1228 			if ( rc != SQL_SUCCESS ) {
1229 				rs->sr_err = LDAP_OTHER;
1230 				rs->sr_text = "SQL-backend error";
1231 				e = NULL;
1232 				goto done;
1233 			}
1234 
1235 			rc = SQLExecute( sth );
1236 			if ( rc != SQL_SUCCESS ) {
1237 				rs->sr_err = LDAP_OTHER;
1238 				rs->sr_text = "SQL-backend error";
1239 				e = NULL;
1240 				goto done;
1241 			}
1242 		}
1243 
1244 		/*
1245 		 * the query to know the id of the inserted entry
1246 		 * must be embedded in the create procedure
1247 		 */
1248 		rc = SQLNumResultCols( sth, &ncols );
1249 		if ( rc != SQL_SUCCESS ) {
1250 			Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1251 				"create_proc result evaluation failed\n",
1252 				op->ora_e->e_name.bv_val, 0, 0 );
1253 			backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc);
1254 			SQLFreeStmt( sth, SQL_DROP );
1255 			rs->sr_err = LDAP_OTHER;
1256 			rs->sr_text = "SQL-backend error";
1257 			e = NULL;
1258 			goto done;
1259 
1260 		} else if ( ncols != 1 ) {
1261 			Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1262 				"create_proc result is bogus (ncols=%d)\n",
1263 				op->ora_e->e_name.bv_val, ncols, 0 );
1264 			backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc);
1265 			SQLFreeStmt( sth, SQL_DROP );
1266 			rs->sr_err = LDAP_OTHER;
1267 			rs->sr_text = "SQL-backend error";
1268 			e = NULL;
1269 			goto done;
1270 		}
1271 
1272 #if 0
1273 		{
1274 			SQLCHAR		colname[ 64 ];
1275 			SQLSMALLINT	name_len, col_type, col_scale, col_null;
1276 			UDWORD		col_prec;
1277 
1278 			/*
1279 			 * FIXME: check whether col_type is compatible,
1280 			 * if it can be null and so on ...
1281 			 */
1282 			rc = SQLDescribeCol( sth, (SQLUSMALLINT)1,
1283 					&colname[ 0 ],
1284 					(SQLUINTEGER)( sizeof( colname ) - 1 ),
1285 					&name_len, &col_type,
1286 					&col_prec, &col_scale, &col_null );
1287 		}
1288 #endif
1289 
1290 		rc = SQLBindCol( sth, (SQLUSMALLINT)1, SQL_C_ULONG,
1291 				(SQLPOINTER)&new_keyval,
1292 				(SQLINTEGER)sizeof( new_keyval ),
1293 				&value_len );
1294 
1295 		rc = SQLFetch( sth );
1296 
1297 		if ( value_len <= 0 ) {
1298 			Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1299 				"create_proc result is empty?\n",
1300 				op->ora_e->e_name.bv_val, 0, 0 );
1301 			backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc);
1302 			SQLFreeStmt( sth, SQL_DROP );
1303 			rs->sr_err = LDAP_OTHER;
1304 			rs->sr_text = "SQL-backend error";
1305 			e = NULL;
1306 			goto done;
1307 		}
1308 	}
1309 
1310 	SQLFreeStmt( sth, SQL_DROP );
1311 
1312 	Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1313 		"create_proc returned keyval=%ld\n",
1314 		op->ora_e->e_name.bv_val, new_keyval, 0 );
1315 
1316 	rc = backsql_Prepare( dbh, &sth, bi->sql_insentry_stmt, 0 );
1317 	if ( rc != SQL_SUCCESS ) {
1318 		rs->sr_err = LDAP_OTHER;
1319 		rs->sr_text = "SQL-backend error";
1320 		e = NULL;
1321 		goto done;
1322 	}
1323 
1324 	rc = backsql_BindParamBerVal( sth, 1, SQL_PARAM_INPUT, &realdn );
1325 	if ( rc != SQL_SUCCESS ) {
1326 		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1327 			"error binding DN parameter for objectClass %s\n",
1328 			op->ora_e->e_name.bv_val,
1329 			oc->bom_oc->soc_cname.bv_val, 0 );
1330 		backsql_PrintErrors( bi->sql_db_env, dbh,
1331 			sth, rc );
1332 		SQLFreeStmt( sth, SQL_DROP );
1333 
1334 		rs->sr_text = "SQL-backend error";
1335 		rs->sr_err = LDAP_OTHER;
1336 		e = NULL;
1337 		goto done;
1338 	}
1339 
1340 	rc = backsql_BindParamInt( sth, 2, SQL_PARAM_INPUT, &oc->bom_id );
1341 	if ( rc != SQL_SUCCESS ) {
1342 		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1343 			"error binding objectClass ID parameter "
1344 			"for objectClass %s\n",
1345 			op->ora_e->e_name.bv_val,
1346 			oc->bom_oc->soc_cname.bv_val, 0 );
1347 		backsql_PrintErrors( bi->sql_db_env, dbh,
1348 			sth, rc );
1349 		SQLFreeStmt( sth, SQL_DROP );
1350 
1351 		rs->sr_text = "SQL-backend error";
1352 		rs->sr_err = LDAP_OTHER;
1353 		e = NULL;
1354 		goto done;
1355 	}
1356 
1357 	rc = backsql_BindParamID( sth, 3, SQL_PARAM_INPUT, &bsi.bsi_base_id.eid_id );
1358 	if ( rc != SQL_SUCCESS ) {
1359 		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1360 			"error binding parent ID parameter "
1361 			"for objectClass %s\n",
1362 			op->ora_e->e_name.bv_val,
1363 			oc->bom_oc->soc_cname.bv_val, 0 );
1364 		backsql_PrintErrors( bi->sql_db_env, dbh,
1365 			sth, rc );
1366 		SQLFreeStmt( sth, SQL_DROP );
1367 
1368 		rs->sr_text = "SQL-backend error";
1369 		rs->sr_err = LDAP_OTHER;
1370 		e = NULL;
1371 		goto done;
1372 	}
1373 
1374 	rc = backsql_BindParamInt( sth, 4, SQL_PARAM_INPUT, &new_keyval );
1375 	if ( rc != SQL_SUCCESS ) {
1376 		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1377 			"error binding entry ID parameter "
1378 			"for objectClass %s\n",
1379 			op->ora_e->e_name.bv_val,
1380 			oc->bom_oc->soc_cname.bv_val, 0 );
1381 		backsql_PrintErrors( bi->sql_db_env, dbh,
1382 			sth, rc );
1383 		SQLFreeStmt( sth, SQL_DROP );
1384 
1385 		rs->sr_text = "SQL-backend error";
1386 		rs->sr_err = LDAP_OTHER;
1387 		e = NULL;
1388 		goto done;
1389 	}
1390 
1391 	Debug( LDAP_DEBUG_TRACE, "   backsql_add(): executing \"%s\" for dn \"%s\"\n",
1392 			bi->sql_insentry_stmt, op->ora_e->e_name.bv_val, 0 );
1393 #ifdef BACKSQL_ARBITRARY_KEY
1394 	Debug( LDAP_DEBUG_TRACE, "                  for oc_map_id=%ld, "
1395 			"p_id=%s, keyval=%ld\n",
1396 			oc->bom_id, bsi.bsi_base_id.eid_id.bv_val, new_keyval );
1397 #else /* ! BACKSQL_ARBITRARY_KEY */
1398 	Debug( LDAP_DEBUG_TRACE, "                  for oc_map_id=%ld, "
1399 			"p_id=%ld, keyval=%ld\n",
1400 			oc->bom_id, bsi.bsi_base_id.eid_id, new_keyval );
1401 #endif /* ! BACKSQL_ARBITRARY_KEY */
1402 	rc = SQLExecute( sth );
1403 	if ( rc != SQL_SUCCESS ) {
1404 		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1405 			"could not insert ldap_entries record\n",
1406 			op->ora_e->e_name.bv_val, 0, 0 );
1407 		backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
1408 
1409 		/*
1410 		 * execute delete_proc to delete data added !!!
1411 		 */
1412 		SQLFreeStmt( sth, SQL_DROP );
1413 		rs->sr_err = LDAP_OTHER;
1414 		rs->sr_text = "SQL-backend error";
1415 		e = NULL;
1416 		goto done;
1417 	}
1418 
1419 	SQLFreeStmt( sth, SQL_DROP );
1420 
1421 	for ( at = op->ora_e->e_attrs; at != NULL; at = at->a_next ) {
1422 		Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
1423 			"adding attribute \"%s\"\n",
1424 			at->a_desc->ad_cname.bv_val, 0, 0 );
1425 
1426 		/*
1427 		 * Skip:
1428 		 * - the first occurrence of objectClass, which is used
1429 		 *   to determine how to build the SQL entry (FIXME ?!?)
1430 		 * - operational attributes
1431 		 * - empty attributes (FIXME ?!?)
1432 		 */
1433 		if ( backsql_attr_skip( at->a_desc, at->a_vals ) ) {
1434 			continue;
1435 		}
1436 
1437 		if ( at->a_desc == slap_schema.si_ad_objectClass ) {
1438 			at_objectClass = at;
1439 			continue;
1440 		}
1441 
1442 		rs->sr_err = backsql_add_attr( op, rs, dbh, oc, at, new_keyval );
1443 		if ( rs->sr_err != LDAP_SUCCESS ) {
1444 			e = op->ora_e;
1445 			goto done;
1446 		}
1447 	}
1448 
1449 	if ( at_objectClass ) {
1450 		rs->sr_err = backsql_add_attr( op, rs, dbh, oc,
1451 				at_objectClass, new_keyval );
1452 		if ( rs->sr_err != LDAP_SUCCESS ) {
1453 			e = op->ora_e;
1454 			goto done;
1455 		}
1456 	}
1457 
1458 done:;
1459 	/*
1460 	 * Commit only if all operations succeed
1461 	 */
1462 	if ( sth != SQL_NULL_HSTMT ) {
1463 		SQLUSMALLINT	CompletionType = SQL_ROLLBACK;
1464 
1465 		if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
1466 			assert( e == NULL );
1467 			CompletionType = SQL_COMMIT;
1468 		}
1469 
1470 		SQLTransact( SQL_NULL_HENV, dbh, CompletionType );
1471 	}
1472 
1473 	/*
1474 	 * FIXME: NOOP does not work for add -- it works for all
1475 	 * the other operations, and I don't get the reason :(
1476 	 *
1477 	 * hint: there might be some autocommit in Postgres
1478 	 * so that when the unique id of the key table is
1479 	 * automatically increased, there's no rollback.
1480 	 * We might implement a "rollback" procedure consisting
1481 	 * in deleting that row.
1482 	 */
1483 
1484 	if ( e != NULL ) {
1485 		int	disclose = 1;
1486 
1487 		if ( e == op->ora_e && !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
1488 			/* mask already collected */
1489 			disclose = 0;
1490 
1491 		} else if ( e == &p && !access_allowed( op, &p,
1492 					slap_schema.si_ad_entry, NULL,
1493 					ACL_DISCLOSE, NULL ) )
1494 		{
1495 			disclose = 0;
1496 		}
1497 
1498 		if ( disclose == 0 ) {
1499 			rs->sr_err = LDAP_NO_SUCH_OBJECT;
1500 			rs->sr_text = NULL;
1501 			rs->sr_matched = NULL;
1502 			if ( rs->sr_ref ) {
1503 				ber_bvarray_free( rs->sr_ref );
1504 				rs->sr_ref = NULL;
1505 			}
1506 		}
1507 	}
1508 
1509 	if ( op->o_noop && rs->sr_err == LDAP_SUCCESS ) {
1510 		rs->sr_err = LDAP_X_NO_OPERATION;
1511 	}
1512 
1513 	send_ldap_result( op, rs );
1514 	slap_graduate_commit_csn( op );
1515 
1516 	if ( !BER_BVISNULL( &realdn )
1517 			&& realdn.bv_val != op->ora_e->e_name.bv_val )
1518 	{
1519 		ch_free( realdn.bv_val );
1520 	}
1521 
1522 	if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
1523 		(void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
1524 	}
1525 
1526 	if ( !BER_BVISNULL( &p.e_nname ) ) {
1527 		backsql_entry_clean( op, &p );
1528 	}
1529 
1530 	Debug( LDAP_DEBUG_TRACE, "<==backsql_add(\"%s\"): %d \"%s\"\n",
1531 			op->ora_e->e_name.bv_val,
1532 			rs->sr_err,
1533 			rs->sr_text ? rs->sr_text : "" );
1534 
1535 	rs->sr_text = NULL;
1536 	rs->sr_matched = NULL;
1537 	if ( rs->sr_ref ) {
1538 		ber_bvarray_free( rs->sr_ref );
1539 		rs->sr_ref = NULL;
1540 	}
1541 
1542 	return rs->sr_err;
1543 }
1544 
1545