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