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