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