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