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