add.c 40.3 KB
Newer Older
1
2
3
/* $OpenLDAP$ */
/* This work is part of OpenLDAP Software <http://www.openldap.org/>.
 *
Kurt Zeilenga's avatar
Kurt Zeilenga committed
4
 * Copyright 1999-2013 The OpenLDAP Foundation.
5
 * Portions Copyright 1999 Dmitry Kovalev.
6
 * Portions Copyright 2002 Pierangelo Masarati.
Kurt Zeilenga's avatar
Kurt Zeilenga committed
7
 * Portions Copyright 2004 Mark Adamson.
8
9
10
11
12
13
14
15
16
17
18
19
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted only as authorized by the OpenLDAP
 * Public License.
 *
 * A copy of this license is available in the file LICENSE in the
 * top-level directory of the distribution or, alternatively, at
 * <http://www.OpenLDAP.org/license.html>.
 */
/* ACKNOWLEDGEMENTS:
 * This work was initially developed by Dmitry Kovalev for inclusion
20
21
22
 * by OpenLDAP Software.  Additional significant contributors include
 * Pierangelo Masarati and Mark Adamson.

23
24
25
26
27
28
29
30
31
32
33
 */

#include "portable.h"

#include <stdio.h>
#include <sys/types.h>
#include "ac/string.h"

#include "slap.h"
#include "proto-sql.h"

34
35
36
37
#ifdef BACKSQL_SYNCPROV
#include <lutil.h>
#endif /* BACKSQL_SYNCPROV */

38
39
/*
 * Skip:
Pierangelo Masarati's avatar
Pierangelo Masarati committed
40
41
42
 * - null values (e.g. delete modification)
 * - single occurrence of objectClass, because it is already used
 *   to determine how to build the SQL entry
43
 * - operational attributes
Pierangelo Masarati's avatar
Pierangelo Masarati committed
44
 * - empty attributes
45
 */
46
47
#define backsql_opattr_skip(ad) \
	(is_at_operational( (ad)->ad_type ) && (ad) != slap_schema.si_ad_ref )
Pierangelo Masarati's avatar
Pierangelo Masarati committed
48
#define	backsql_attr_skip(ad, vals) \
49
50
	( \
		( (ad) == slap_schema.si_ad_objectClass \
Pierangelo Masarati's avatar
Pierangelo Masarati committed
51
				&& (vals) && BER_BVISNULL( &((vals)[ 1 ]) ) ) \
52
		|| backsql_opattr_skip( (ad) ) \
53
		|| ( (vals) && BER_BVISNULL( &((vals)[ 0 ]) ) ) \
54
55
	)

56
57
58
59
60
61
62
63
64
65
int
backsql_modify_delete_all_values(
	Operation 		*op,
	SlapReply		*rs,
	SQLHDBC			dbh, 
	backsql_entryID		*e_id,
	backsql_at_map_rec	*at )
{
	backsql_info	*bi = (backsql_info *)op->o_bd->be_private;
	RETCODE		rc;
66
	SQLHSTMT	asth = SQL_NULL_HSTMT;
67
68
	BACKSQL_ROW_NTS	row;

69
	assert( at != NULL );
70
71
72
73
74
75
76
77
78
79
80
81
82
83
	if ( at->bam_delete_proc == NULL ) {
		Debug( LDAP_DEBUG_TRACE,
			"   backsql_modify_delete_all_values(): "
			"missing attribute value delete procedure "
			"for attr \"%s\"\n",
			at->bam_ad->ad_cname.bv_val, 0, 0 );
		if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
			rs->sr_text = "SQL-backend error";
			return rs->sr_err = LDAP_OTHER;
		}

		return LDAP_SUCCESS;
	}

84
85
86
87
	rc = backsql_Prepare( dbh, &asth, at->bam_query, 0 );
	if ( rc != SQL_SUCCESS ) {
		Debug( LDAP_DEBUG_TRACE,
			"   backsql_modify_delete_all_values(): "
88
89
90
			"error preparing attribute value select query "
			"\"%s\"\n",
			at->bam_query, 0, 0 );
91
		backsql_PrintErrors( bi->sql_db_env, dbh, 
92
93
				asth, rc );

94
95
		rs->sr_text = "SQL-backend error";
		return rs->sr_err = LDAP_OTHER;
96
97
	}

98
	rc = backsql_BindParamID( asth, 1, SQL_PARAM_INPUT, &e_id->eid_keyval );
99
100
101
	if ( rc != SQL_SUCCESS ) {
		Debug( LDAP_DEBUG_TRACE,
			"   backsql_modify_delete_all_values(): "
102
103
			"error binding key value parameter "
			"to attribute value select query\n",
104
			0, 0, 0 );
105
		backsql_PrintErrors( bi->sql_db_env, dbh, 
106
107
108
				asth, rc );
		SQLFreeStmt( asth, SQL_DROP );

109
110
		rs->sr_text = "SQL-backend error";
		return rs->sr_err = LDAP_OTHER;
111
112
113
114
115
116
	}
			
	rc = SQLExecute( asth );
	if ( !BACKSQL_SUCCESS( rc ) ) {
		Debug( LDAP_DEBUG_TRACE,
			"   backsql_modify_delete_all_values(): "
117
			"error executing attribute value select query\n",
118
			0, 0, 0 );
119
		backsql_PrintErrors( bi->sql_db_env, dbh, 
120
121
122
				asth, rc );
		SQLFreeStmt( asth, SQL_DROP );

123
124
		rs->sr_text = "SQL-backend error";
		return rs->sr_err = LDAP_OTHER;
125
126
	}

127
	backsql_BindRowAsStrings_x( asth, &row, op->o_tmpmemctx );
128
129
130
131
	for ( rc = SQLFetch( asth );
			BACKSQL_SUCCESS( rc );
			rc = SQLFetch( asth ) )
	{
132
		int		i;
133
		/* first parameter no, parameter order */
134
		SQLUSMALLINT	pno = 0,
135
				po = 0;
136
		/* procedure return code */
137
		int		prc = LDAP_SUCCESS;
138
139
		
		for ( i = 0; i < row.ncols; i++ ) {
140
			SQLHSTMT	sth = SQL_NULL_HSTMT;
141
			ber_len_t	col_len;
142
143
144
145
146
			
			rc = backsql_Prepare( dbh, &sth, at->bam_delete_proc, 0 );
			if ( rc != SQL_SUCCESS ) {
				Debug( LDAP_DEBUG_TRACE,
					"   backsql_modify_delete_all_values(): "
147
148
149
					"error preparing attribute value "
					"delete procedure "
					"\"%s\"\n",
150
					at->bam_delete_proc, 0, 0 );
151
				backsql_PrintErrors( bi->sql_db_env, dbh, 
152
153
						sth, rc );

154
				rs->sr_text = "SQL-backend error";
155
156
				rs->sr_err = LDAP_OTHER;
				goto done;
157
158
159
160
			}

	   		if ( BACKSQL_IS_DEL( at->bam_expect_return ) ) {
				pno = 1;
161
162
163
164
165
166
167
				rc = backsql_BindParamInt( sth, 1,
						SQL_PARAM_OUTPUT, &prc );
				if ( rc != SQL_SUCCESS ) {
					Debug( LDAP_DEBUG_TRACE,
						"   backsql_modify_delete_all_values(): "
						"error binding output parameter for %s[%d]\n",
						at->bam_ad->ad_cname.bv_val, i, 0 );
168
					backsql_PrintErrors( bi->sql_db_env, dbh, 
169
170
171
						sth, rc );
					SQLFreeStmt( sth, SQL_DROP );

172
					rs->sr_text = "SQL-backend error";
173
174
					rs->sr_err = LDAP_OTHER;
					goto done;
175
				}
176
177
			}
			po = ( BACKSQL_IS_DEL( at->bam_param_order ) ) > 0;
178
179
180
181
182
183
184
			rc = backsql_BindParamID( sth, pno + 1 + po,
				SQL_PARAM_INPUT, &e_id->eid_keyval );
			if ( rc != SQL_SUCCESS ) {
				Debug( LDAP_DEBUG_TRACE,
					"   backsql_modify_delete_all_values(): "
					"error binding keyval parameter for %s[%d]\n",
					at->bam_ad->ad_cname.bv_val, i, 0 );
185
				backsql_PrintErrors( bi->sql_db_env, dbh, 
186
187
188
					sth, rc );
				SQLFreeStmt( sth, SQL_DROP );

189
				rs->sr_text = "SQL-backend error";
190
191
				rs->sr_err = LDAP_OTHER;
				goto done;
192
			}
193

194
			Debug( LDAP_DEBUG_TRACE,
195
				"   backsql_modify_delete_all_values() "
196
197
198
				"arg(%d)=" BACKSQL_IDFMT "\n",
				pno + 1 + po,
				BACKSQL_IDARG(e_id->eid_keyval), 0 );
199
200
201
202
203

			/*
			 * check for syntax needed here 
			 * maybe need binary bind?
			 */
204
			col_len = strlen( row.cols[ i ] );
205
206
207
208
209
210
211
			rc = backsql_BindParamStr( sth, pno + 2 - po,
				SQL_PARAM_INPUT, row.cols[ i ], col_len );
			if ( rc != SQL_SUCCESS ) {
				Debug( LDAP_DEBUG_TRACE,
					"   backsql_modify_delete_all_values(): "
					"error binding value parameter for %s[%d]\n",
					at->bam_ad->ad_cname.bv_val, i, 0 );
212
				backsql_PrintErrors( bi->sql_db_env, dbh, 
213
214
215
					sth, rc );
				SQLFreeStmt( sth, SQL_DROP );

216
				rs->sr_text = "SQL-backend error";
217
218
				rs->sr_err = LDAP_OTHER;
				goto done;
219
			}
220
221
222
	 
			Debug( LDAP_DEBUG_TRACE, 
				"   backsql_modify_delete_all_values(): "
223
				"arg(%d)=%s; executing \"%s\"\n",
224
225
226
				pno + 2 - po, row.cols[ i ],
				at->bam_delete_proc );
			rc = SQLExecute( sth );
227
228
229
230
			if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
				rs->sr_err = LDAP_SUCCESS;

			} else {
231
232
233
				Debug( LDAP_DEBUG_TRACE,
					"   backsql_modify_delete_all_values(): "
					"delete_proc "
234
235
236
237
238
239
240
241
242
243
					"execution failed (rc=%d, prc=%d)\n",
					rc, prc, 0 );
				if ( prc != LDAP_SUCCESS ) {
					/* SQL procedure executed fine 
					 * but returned an error */
					rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );

				} else {
					backsql_PrintErrors( bi->sql_db_env, dbh,
							sth, rc );
244
					rs->sr_err = LDAP_OTHER;
245
				}
246
				rs->sr_text = op->o_req_dn.bv_val;
247
				SQLFreeStmt( sth, SQL_DROP );
248
				goto done;
249
250
251
252
			}
			SQLFreeStmt( sth, SQL_DROP );
		}
	}
253
254
255
256
257

	rs->sr_err = LDAP_SUCCESS;

done:;
	backsql_FreeRow_x( &row, op->o_tmpmemctx );
258
259
	SQLFreeStmt( asth, SQL_DROP );

260
	return rs->sr_err;
261
262
}

263
264
265
266
267
268
269
270
271
int
backsql_modify_internal(
	Operation 		*op,
	SlapReply		*rs,
	SQLHDBC			dbh, 
	backsql_oc_map_rec	*oc,
	backsql_entryID		*e_id,
	Modifications		*modlist )
{
272
	backsql_info	*bi = (backsql_info *)op->o_bd->be_private;
273
274
275
276
277
278
279
280
	RETCODE		rc;
	Modifications	*ml;

	Debug( LDAP_DEBUG_TRACE, "==>backsql_modify_internal(): "
		"traversing modifications list\n", 0, 0, 0 );

	for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
		AttributeDescription	*ad;
281
282
283
284
285
		int			sm_op;
		static char		*sm_ops[] = { "add", "delete", "replace", "increment", NULL };

		BerVarray		sm_values;
#if 0
Pierangelo Masarati's avatar
Pierangelo Masarati committed
286
		/* NOTE: some day we'll have to pass 
287
		 * the normalized values as well */
288
		BerVarray		sm_nvalues;
289
#endif
290
291
292
		backsql_at_map_rec	*at = NULL;
		struct berval		*at_val;
		int			i;
293
		
294
295
296
297
298
299
		ad = ml->sml_mod.sm_desc;
		sm_op = ( ml->sml_mod.sm_op & LDAP_MOD_OP );
		sm_values = ml->sml_mod.sm_values;
#if 0
		sm_nvalues = ml->sml_mod.sm_nvalues;
#endif
300
301

		Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
302
			"modifying attribute \"%s\" (%s) according to "
303
			"mappings for objectClass \"%s\"\n",
304
			ad->ad_cname.bv_val, sm_ops[ sm_op ], BACKSQL_OC_NAME( oc ) );
305

306
		if ( backsql_attr_skip( ad, sm_values ) ) {
307
308
309
310
311
312
313
314
			continue;
		}

  		at = backsql_ad2at( oc, ad );
		if ( at == NULL ) {
			Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
				"attribute \"%s\" is not registered "
				"in objectClass \"%s\"\n",
315
				ad->ad_cname.bv_val, BACKSQL_OC_NAME( oc ), 0 );
316
317
318
319
320
321
322
323
324
325
326

			if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
				rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
				rs->sr_text = "operation not permitted "
					"within namingContext";
				goto done;
			}

			continue;
		}
  
327
		switch ( sm_op ) {
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
		case LDAP_MOD_REPLACE: {
			Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
				"replacing values for attribute \"%s\"\n",
				at->bam_ad->ad_cname.bv_val, 0, 0 );

			if ( at->bam_add_proc == NULL ) {
				Debug( LDAP_DEBUG_TRACE,
					"   backsql_modify_internal(): "
					"add procedure is not defined "
					"for attribute \"%s\" "
					"- unable to perform replacements\n",
					at->bam_ad->ad_cname.bv_val, 0, 0 );

				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
					rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
					rs->sr_text = "operation not permitted "
						"within namingContext";
					goto done;
				}

				break;
			}

			if ( at->bam_delete_proc == NULL ) {
				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
					Debug( LDAP_DEBUG_TRACE,
						"   backsql_modify_internal(): "
						"delete procedure is not defined "
						"for attribute \"%s\"\n",
						at->bam_ad->ad_cname.bv_val, 0, 0 );

					rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
					rs->sr_text = "operation not permitted "
						"within namingContext";
					goto done;
				}

				Debug( LDAP_DEBUG_TRACE,
					"   backsql_modify_internal(): "
					"delete procedure is not defined "
					"for attribute \"%s\" "
					"- adding only\n",
					at->bam_ad->ad_cname.bv_val, 0, 0 );

				goto add_only;
			}
374

375
del_all:
376
377
378
			rs->sr_err = backsql_modify_delete_all_values( op, rs, dbh, e_id, at );
			if ( rs->sr_err != LDAP_SUCCESS ) {
				goto done;
379
			}
380
381

			/* LDAP_MOD_DELETE gets here if all values must be deleted */
382
			if ( sm_op == LDAP_MOD_DELETE ) {
383
384
				break;
			}
385
386
387
388
389
390
	       	}

		/*
		 * PASSTHROUGH - to add new attributes -- do NOT add break
		 */
		case LDAP_MOD_ADD:
391
		/* case SLAP_MOD_SOFTADD: */
392
		/* case SLAP_MOD_ADD_IF_NOT_PRESENT: */
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
add_only:;
			if ( at->bam_add_proc == NULL ) {
				Debug( LDAP_DEBUG_TRACE,
					"   backsql_modify_internal(): "
					"add procedure is not defined "
					"for attribute \"%s\"\n",
					at->bam_ad->ad_cname.bv_val, 0, 0 );

				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
					rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
					rs->sr_text = "operation not permitted "
						"within namingContext";
					goto done;
				}

				break;
			}
			
			Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
				"adding new values for attribute \"%s\"\n",
				at->bam_ad->ad_cname.bv_val, 0, 0 );
Pierangelo Masarati's avatar
Pierangelo Masarati committed
414
415
416
417

			/* can't add a NULL val array */
			assert( sm_values != NULL );
			
418
419
420
421
			for ( i = 0, at_val = sm_values;
					!BER_BVISNULL( at_val ); 
					i++, at_val++ )
			{
422
423
424
425
426
427
428
				SQLHSTMT	sth = SQL_NULL_HSTMT;
				/* first parameter position, parameter order */
				SQLUSMALLINT	pno = 0,
						po;
				/* procedure return code */
				int		prc = LDAP_SUCCESS;

429
430
431
432
433
434
				rc = backsql_Prepare( dbh, &sth, at->bam_add_proc, 0 );
				if ( rc != SQL_SUCCESS ) {
					Debug( LDAP_DEBUG_TRACE,
						"   backsql_modify_internal(): "
						"error preparing add query\n", 
						0, 0, 0 );
435
					backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
436
437
438
439
440
441

					rs->sr_err = LDAP_OTHER;
					rs->sr_text = "SQL-backend error";
					goto done;
				}

442
443
				if ( BACKSQL_IS_ADD( at->bam_expect_return ) ) {
					pno = 1;
444
445
446
447
448
449
450
	      				rc = backsql_BindParamInt( sth, 1,
						SQL_PARAM_OUTPUT, &prc );
					if ( rc != SQL_SUCCESS ) {
						Debug( LDAP_DEBUG_TRACE,
							"   backsql_modify_internal(): "
							"error binding output parameter for %s[%d]\n",
							at->bam_ad->ad_cname.bv_val, i, 0 );
451
						backsql_PrintErrors( bi->sql_db_env, dbh, 
452
453
454
455
456
457
458
							sth, rc );
						SQLFreeStmt( sth, SQL_DROP );

						rs->sr_text = "SQL-backend error";
						rs->sr_err = LDAP_OTHER;
						goto done;
					}
459
460
				}
				po = ( BACKSQL_IS_ADD( at->bam_param_order ) ) > 0;
461
462
463
464
465
466
467
				rc = backsql_BindParamID( sth, pno + 1 + po,
					SQL_PARAM_INPUT, &e_id->eid_keyval );
				if ( rc != SQL_SUCCESS ) {
					Debug( LDAP_DEBUG_TRACE,
						"   backsql_modify_internal(): "
						"error binding keyval parameter for %s[%d]\n",
						at->bam_ad->ad_cname.bv_val, i, 0 );
468
					backsql_PrintErrors( bi->sql_db_env, dbh, 
469
470
471
472
473
474
475
						sth, rc );
					SQLFreeStmt( sth, SQL_DROP );

					rs->sr_text = "SQL-backend error";
					rs->sr_err = LDAP_OTHER;
					goto done;
				}
476

477
478
				Debug( LDAP_DEBUG_TRACE,
					"   backsql_modify_internal(): "
479
480
481
					"arg(%d)=" BACKSQL_IDFMT "\n", 
					pno + 1 + po,
					BACKSQL_IDARG(e_id->eid_keyval), 0 );
482
483
484
485
486

				/*
				 * check for syntax needed here
				 * maybe need binary bind?
				 */
487
488
489
490
491
492
493
				rc = backsql_BindParamBerVal( sth, pno + 2 - po,
					SQL_PARAM_INPUT, at_val );
				if ( rc != SQL_SUCCESS ) {
					Debug( LDAP_DEBUG_TRACE,
						"   backsql_modify_internal(): "
						"error binding value parameter for %s[%d]\n",
						at->bam_ad->ad_cname.bv_val, i, 0 );
494
					backsql_PrintErrors( bi->sql_db_env, dbh, 
495
496
497
498
499
500
501
						sth, rc );
					SQLFreeStmt( sth, SQL_DROP );

					rs->sr_text = "SQL-backend error";
					rs->sr_err = LDAP_OTHER;
					goto done;
				}
502
503
				Debug( LDAP_DEBUG_TRACE,
					"   backsql_modify_internal(): "
504
					"arg(%d)=\"%s\"; executing \"%s\"\n", 
505
506
507
					pno + 2 - po, at_val->bv_val,
					at->bam_add_proc );

508
				rc = SQLExecute( sth );
509
510
511
512
				if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
					rs->sr_err = LDAP_SUCCESS;

				} else {
513
514
					Debug( LDAP_DEBUG_TRACE,
						"   backsql_modify_internal(): "
515
516
517
518
519
520
521
						"add_proc execution failed "
						"(rc=%d, prc=%d)\n",
						rc, prc, 0 );
					if ( prc != LDAP_SUCCESS ) {
						/* SQL procedure executed fine 
						 * but returned an error */
						SQLFreeStmt( sth, SQL_DROP );
522

523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
						rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
						rs->sr_text = at->bam_ad->ad_cname.bv_val;
						return rs->sr_err;
					
					} else {
						backsql_PrintErrors( bi->sql_db_env, dbh,
								sth, rc );
						if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) 
						{
							SQLFreeStmt( sth, SQL_DROP );

							rs->sr_err = LDAP_OTHER;
							rs->sr_text = "SQL-backend error";
							goto done;
						}
					}
539
540
541
542
543
544
				}
				SQLFreeStmt( sth, SQL_DROP );
			}
			break;
			
	      	case LDAP_MOD_DELETE:
545
		/* case SLAP_MOD_SOFTDEL: */
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
			if ( at->bam_delete_proc == NULL ) {
				Debug( LDAP_DEBUG_TRACE,
					"   backsql_modify_internal(): "
					"delete procedure is not defined "
					"for attribute \"%s\"\n",
					at->bam_ad->ad_cname.bv_val, 0, 0 );

				if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
					rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
					rs->sr_text = "operation not permitted "
						"within namingContext";
					goto done;
				}

				break;
			}

563
			if ( sm_values == NULL ) {
564
565
566
567
568
569
570
571
572
573
574
575
576
				Debug( LDAP_DEBUG_TRACE,
					"   backsql_modify_internal(): "
					"no values given to delete "
					"for attribute \"%s\" "
					"-- deleting all values\n",
					at->bam_ad->ad_cname.bv_val, 0, 0 );
				goto del_all;
			}

			Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
				"deleting values for attribute \"%s\"\n",
				at->bam_ad->ad_cname.bv_val, 0, 0 );

577
578
579
580
			for ( i = 0, at_val = sm_values;
					!BER_BVISNULL( at_val );
					i++, at_val++ )
			{
581
582
583
584
585
586
587
				SQLHSTMT	sth = SQL_NULL_HSTMT;
				/* first parameter position, parameter order */
				SQLUSMALLINT	pno = 0,
						po;
				/* procedure return code */
				int		prc = LDAP_SUCCESS;

588
589
590
591
592
593
				rc = backsql_Prepare( dbh, &sth, at->bam_delete_proc, 0 );
				if ( rc != SQL_SUCCESS ) {
					Debug( LDAP_DEBUG_TRACE,
						"   backsql_modify_internal(): "
						"error preparing delete query\n", 
						0, 0, 0 );
594
					backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
595
596
597
598
599
600

					rs->sr_err = LDAP_OTHER;
					rs->sr_text = "SQL-backend error";
					goto done;
				}

601
602
				if ( BACKSQL_IS_DEL( at->bam_expect_return ) ) {
					pno = 1;
603
604
605
606
607
608
609
					rc = backsql_BindParamInt( sth, 1,
						SQL_PARAM_OUTPUT, &prc );
					if ( rc != SQL_SUCCESS ) {
						Debug( LDAP_DEBUG_TRACE,
							"   backsql_modify_internal(): "
							"error binding output parameter for %s[%d]\n",
							at->bam_ad->ad_cname.bv_val, i, 0 );
610
						backsql_PrintErrors( bi->sql_db_env, dbh, 
611
612
613
614
615
616
617
							sth, rc );
						SQLFreeStmt( sth, SQL_DROP );

						rs->sr_text = "SQL-backend error";
						rs->sr_err = LDAP_OTHER;
						goto done;
					}
618
619
				}
				po = ( BACKSQL_IS_DEL( at->bam_param_order ) ) > 0;
620
621
622
623
624
625
626
				rc = backsql_BindParamID( sth, pno + 1 + po,
					SQL_PARAM_INPUT, &e_id->eid_keyval );
				if ( rc != SQL_SUCCESS ) {
					Debug( LDAP_DEBUG_TRACE,
						"   backsql_modify_internal(): "
						"error binding keyval parameter for %s[%d]\n",
						at->bam_ad->ad_cname.bv_val, i, 0 );
627
					backsql_PrintErrors( bi->sql_db_env, dbh, 
628
629
630
631
632
633
634
						sth, rc );
					SQLFreeStmt( sth, SQL_DROP );

					rs->sr_text = "SQL-backend error";
					rs->sr_err = LDAP_OTHER;
					goto done;
				}
635

636
637
				Debug( LDAP_DEBUG_TRACE,
					"   backsql_modify_internal(): "
638
639
640
					"arg(%d)=" BACKSQL_IDFMT "\n", 
					pno + 1 + po,
					BACKSQL_IDARG(e_id->eid_keyval), 0 );
641
642
643
644
645

				/*
				 * check for syntax needed here 
				 * maybe need binary bind?
				 */
646
647
648
649
650
651
652
				rc = backsql_BindParamBerVal( sth, pno + 2 - po,
					SQL_PARAM_INPUT, at_val );
				if ( rc != SQL_SUCCESS ) {
					Debug( LDAP_DEBUG_TRACE,
						"   backsql_modify_internal(): "
						"error binding value parameter for %s[%d]\n",
						at->bam_ad->ad_cname.bv_val, i, 0 );
653
					backsql_PrintErrors( bi->sql_db_env, dbh, 
654
655
656
						sth, rc );
					SQLFreeStmt( sth, SQL_DROP );

657
658
659
					rs->sr_text = "SQL-backend error";
					rs->sr_err = LDAP_OTHER;
					goto done;
660
				}
661
662
663
664
665

				Debug( LDAP_DEBUG_TRACE,
					"   backsql_modify_internal(): "
					"executing \"%s\"\n", 
					at->bam_delete_proc, 0, 0 );
666
				rc = SQLExecute( sth );
667
668
669
670
671
				if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS )
				{
					rs->sr_err = LDAP_SUCCESS;
					
				} else {
672
673
674
					Debug( LDAP_DEBUG_TRACE,
						"   backsql_modify_internal(): "
						"delete_proc execution "
675
676
677
678
679
680
681
682
						"failed (rc=%d, prc=%d)\n",
						rc, prc, 0 );

					if ( prc != LDAP_SUCCESS ) {
						/* SQL procedure executed fine
						 * but returned an error */
						rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
						rs->sr_text = at->bam_ad->ad_cname.bv_val;
683
						goto done;
684
685
686
687
						
					} else {
						backsql_PrintErrors( bi->sql_db_env,
								dbh, sth, rc );
688
689
690
691
						SQLFreeStmt( sth, SQL_DROP );
						rs->sr_err = LDAP_OTHER;
						rs->sr_text = at->bam_ad->ad_cname.bv_val;
						goto done;
692
693
694
					}
				}
				SQLFreeStmt( sth, SQL_DROP );
695
696
697
698
699
700
701
702
703
704
			}
			break;

	      	case LDAP_MOD_INCREMENT:
			Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
				"increment not supported yet\n", 0, 0, 0 );
			if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
				rs->sr_err = LDAP_OTHER;
				rs->sr_text = "SQL-backend error";
				goto done;
705
706
707
708
709
710
			}
			break;
		}
	}

done:;
711
712
713
	Debug( LDAP_DEBUG_TRACE, "<==backsql_modify_internal(): %d%s%s\n",
		rs->sr_err,
		rs->sr_text ? ": " : "",
714
715
716
717
718
719
720
721
		rs->sr_text ? rs->sr_text : "" );

	/*
	 * FIXME: should fail in case one change fails?
	 */
	return rs->sr_err;
}

722
723
724
725
726
727
728
static int
backsql_add_attr(
	Operation		*op,
	SlapReply		*rs,
	SQLHDBC 		dbh,
	backsql_oc_map_rec 	*oc,
	Attribute		*at,
729
	backsql_key_t		new_keyval )
730
731
732
733
734
735
736
{
	backsql_info		*bi = (backsql_info*)op->o_bd->be_private;
	backsql_at_map_rec	*at_rec = NULL;
	struct berval		*at_val;
	unsigned long		i;
	RETCODE			rc;
	SQLUSMALLINT		currpos;
737
	SQLHSTMT 		sth = SQL_NULL_HSTMT;
738
739
740
741

	at_rec = backsql_ad2at( oc, at->a_desc ); 
  
	if ( at_rec == NULL ) {
742
		Debug( LDAP_DEBUG_TRACE, "   backsql_add_attr(\"%s\"): "
743
744
			"attribute \"%s\" is not registered "
			"in objectclass \"%s\"\n",
745
			op->ora_e->e_name.bv_val,
746
747
748
749
750
751
752
753
754
755
756
757
758
			at->a_desc->ad_cname.bv_val,
			BACKSQL_OC_NAME( oc ) );

		if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
			rs->sr_text = "operation not permitted "
				"within namingContext";
			return rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
		}

		return LDAP_SUCCESS;
	}
	
	if ( at_rec->bam_add_proc == NULL ) {
759
		Debug( LDAP_DEBUG_TRACE, "   backsql_add_attr(\"%s\"): "
760
761
762
			"add procedure is not defined "
			"for attribute \"%s\" "
			"of structuralObjectClass \"%s\"\n",
763
			op->ora_e->e_name.bv_val,
764
765
766
767
768
769
770
771
772
773
774
775
776
			at->a_desc->ad_cname.bv_val,
			BACKSQL_OC_NAME( oc ) );

		if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
			rs->sr_text = "operation not permitted "
				"within namingContext";
			return rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
		}

		return LDAP_SUCCESS;
	}

	for ( i = 0, at_val = &at->a_vals[ i ];
777
		       	!BER_BVISNULL( at_val );
778
779
			i++, at_val = &at->a_vals[ i ] )
	{
780
781
782
783
		/* procedure return code */
		int		prc = LDAP_SUCCESS;
		/* first parameter #, parameter order */
		SQLUSMALLINT	pno, po;
784
		char		logbuf[ STRLENOF("val[], id=") + 2*LDAP_PVT_INTTYPE_CHARS(unsigned long)];
785
786
787
788
789
790
		
		/*
		 * Do not deal with the objectClass that is used
		 * to build the entry
		 */
		if ( at->a_desc == slap_schema.si_ad_objectClass ) {
791
			if ( dn_match( at_val, &oc->bom_oc->soc_cname ) )
792
793
794
795
796
			{
				continue;
			}
		}

797
		rc = backsql_Prepare( dbh, &sth, at_rec->bam_add_proc, 0 );
798
		if ( rc != SQL_SUCCESS ) {
799
800
			rs->sr_text = "SQL-backend error";
			return rs->sr_err = LDAP_OTHER;
801
802
803
804
		}

		if ( BACKSQL_IS_ADD( at_rec->bam_expect_return ) ) {
			pno = 1;
805
806
807
808
			rc = backsql_BindParamInt( sth, 1, SQL_PARAM_OUTPUT, &prc );
			if ( rc != SQL_SUCCESS ) {
				Debug( LDAP_DEBUG_TRACE,
					"   backsql_add_attr(): "
809
					"error binding output parameter for %s[%lu]\n",
810
					at_rec->bam_ad->ad_cname.bv_val, i, 0 );
811
				backsql_PrintErrors( bi->sql_db_env, dbh, 
812
813
814
					sth, rc );
				SQLFreeStmt( sth, SQL_DROP );

815
816
				rs->sr_text = "SQL-backend error";
				return rs->sr_err = LDAP_OTHER;
817
818
			}

819
820
821
822
823
824
		} else {
			pno = 0;
		}

		po = ( BACKSQL_IS_ADD( at_rec->bam_param_order ) ) > 0;
		currpos = pno + 1 + po;
825
		rc = backsql_BindParamNumID( sth, currpos,
826
827
828
829
				SQL_PARAM_INPUT, &new_keyval );
		if ( rc != SQL_SUCCESS ) {
			Debug( LDAP_DEBUG_TRACE,
				"   backsql_add_attr(): "
830
				"error binding keyval parameter for %s[%lu]\n",
831
				at_rec->bam_ad->ad_cname.bv_val, i, 0 );
832
			backsql_PrintErrors( bi->sql_db_env, dbh, 
833
834
835
				sth, rc );
			SQLFreeStmt( sth, SQL_DROP );

836
837
			rs->sr_text = "SQL-backend error";
			return rs->sr_err = LDAP_OTHER;
838
839
		}

840
841
842
843
844
845
846
		currpos = pno + 2 - po;

		/*
		 * check for syntax needed here 
		 * maybe need binary bind?
		 */

847
848
849
850
		rc = backsql_BindParamBerVal( sth, currpos, SQL_PARAM_INPUT, at_val );
		if ( rc != SQL_SUCCESS ) {
			Debug( LDAP_DEBUG_TRACE,
				"   backsql_add_attr(): "
851
				"error binding value parameter for %s[%lu]\n",
852
				at_rec->bam_ad->ad_cname.bv_val, i, 0 );
853
			backsql_PrintErrors( bi->sql_db_env, dbh, 
854
855
856
				sth, rc );
			SQLFreeStmt( sth, SQL_DROP );

857
858
			rs->sr_text = "SQL-backend error";
			return rs->sr_err = LDAP_OTHER;
859
		}
860
861

#ifdef LDAP_DEBUG
862
863
864
865
866
867
868
869
		if ( LogTest( LDAP_DEBUG_TRACE ) ) {
			snprintf( logbuf, sizeof( logbuf ), "val[%lu], id=" BACKSQL_IDNUMFMT,
					i, new_keyval );
			Debug( LDAP_DEBUG_TRACE, "   backsql_add_attr(\"%s\"): "
				"executing \"%s\" %s\n", 
				op->ora_e->e_name.bv_val,
				at_rec->bam_add_proc, logbuf );
		}
870
#endif
871
		rc = SQLExecute( sth );
872
873
874
875
		if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
			rs->sr_err = LDAP_SUCCESS;

		} else {
876
			Debug( LDAP_DEBUG_TRACE,
877
				"   backsql_add_attr(\"%s\"): "
878
				"add_proc execution failed (rc=%d, prc=%d)\n", 
879
				op->ora_e->e_name.bv_val, rc, prc );
880
881
882
883
			if ( prc != LDAP_SUCCESS ) {
				/* SQL procedure executed fine
				 * but returned an error */
				rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
884
				rs->sr_text = op->ora_e->e_name.bv_val;
885
				SQLFreeStmt( sth, SQL_DROP );
886
887
888
889
890
				return rs->sr_err;

			} else {
				backsql_PrintErrors( bi->sql_db_env, dbh,
						sth, rc );
891
892
893
894
				rs->sr_err = LDAP_OTHER;
				rs->sr_text = op->ora_e->e_name.bv_val;
				SQLFreeStmt( sth, SQL_DROP );
				return rs->sr_err;
895
896
			}
		}
897
		SQLFreeStmt( sth, SQL_DROP );
898
899
900
901
902
	}

	return LDAP_SUCCESS;
}

903
904
905
906
int
backsql_add( Operation *op, SlapReply *rs )
{
	backsql_info		*bi = (backsql_info*)op->o_bd->be_private;
907
908
	SQLHDBC 		dbh = SQL_NULL_HDBC;
	SQLHSTMT 		sth = SQL_NULL_HSTMT;
909
	backsql_key_t		new_keyval = 0;
910
911
	RETCODE			rc;
	backsql_oc_map_rec 	*oc = NULL;
912
	backsql_srch_info	bsi = { 0 };
913
	Entry			p = { 0 }, *e = NULL;
914
915
	Attribute		*at,
				*at_objectClass = NULL;
916
	ObjectClass		*soc = NULL;
917
	struct berval		scname = BER_BVNULL;
918
	struct berval		pdn;
919
	struct berval		realdn = BER_BVNULL;
920
	int			colnum;
921
	slap_mask_t		mask;
922

923
924
925
	char			textbuf[ SLAP_TEXT_BUFLEN ];
	size_t			textlen = sizeof( textbuf );

926
927
928
929
930
#ifdef BACKSQL_SYNCPROV
	/*
	 * NOTE: fake successful result to force contextCSN to be bumped up
	 */
	if ( op->o_sync ) {
931
		char		buf[ LDAP_PVT_CSNSTR_BUFSIZE ];
Howard Chu's avatar
Howard Chu committed
932
		struct berval	csn;
933

Howard Chu's avatar
Howard Chu committed
934
935
936
		csn.bv_val = buf;
		csn.bv_len = sizeof( buf );
		slap_get_csn( op, &csn, 1 );
937
938
939
940
941
942
943
944
945
946

		rs->sr_err = LDAP_SUCCESS;
		send_ldap_result( op, rs );

		slap_graduate_commit_csn( op );

		return 0;
	}
#endif /* BACKSQL_SYNCPROV */

Pierangelo Masarati's avatar
Pierangelo Masarati committed
947
	Debug( LDAP_DEBUG_TRACE, "==>backsql_add(\"%s\")\n",
948
			op->ora_e->e_name.bv_val, 0, 0 );
949
950

	/* check schema */
951
	if ( BACKSQL_CHECK_SCHEMA( bi ) ) {
952
953
		char		textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };

954
		rs->sr_err = entry_schema_check( op, op->ora_e, NULL, 0, 1, NULL,
955
			&rs->sr_text, textbuf, sizeof( textbuf ) );
956
		if ( rs->sr_err != LDAP_SUCCESS ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
957
			Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
958
				"entry failed schema check -- aborting\n",
959
960
				op->ora_e->e_name.bv_val, 0, 0 );
			e = NULL;
961
962
963
964
			goto done;
		}
	}

965
966
	slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );

967
968
969
970
971
972
973
974
975
976
977
	if ( get_assert( op ) &&
		( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
	{
		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
			"assertion control failed -- aborting\n",
			op->ora_e->e_name.bv_val, 0, 0 );
		e = NULL;
		rs->sr_err = LDAP_ASSERTION_FAILED;
		goto done;
	}

978
	/* search structuralObjectClass */
979
	for ( at = op->ora_e->e_attrs; at != NULL; at = at->a_next ) {
980
981
982
983
984
985
		if ( at->a_desc == slap_schema.si_ad_structuralObjectClass ) {
			break;
		}
	}

	/* there must exist */
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
	if ( at == NULL ) {
		char		buf[ SLAP_TEXT_BUFLEN ];
		const char	*text;

		/* search structuralObjectClass */
		for ( at = op->ora_e->e_attrs; at != NULL; at = at->a_next ) {
			if ( at->a_desc == slap_schema.si_ad_objectClass ) {
				break;
			}
		}

		if ( at == NULL ) {
			Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
				"no objectClass\n",
				op->ora_e->e_name.bv_val, 0, 0 );
			rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
			e = NULL;
			goto done;
		}

1006
		rs->sr_err = structural_class( at->a_vals, &soc, NULL,
1007
				&text, buf, sizeof( buf ), op->o_tmpmemctx );
1008
1009
1010
1011
1012
1013
1014
		if ( rs->sr_err != LDAP_SUCCESS ) {
			Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
				"%s (%d)\n",
				op->ora_e->e_name.bv_val, text, rs->sr_err );
			e = NULL;
			goto done;
		}
1015
		scname = soc->soc_cname;
1016
1017
1018
1019

	} else {
		scname = at->a_vals[0];
	}
1020
1021

	/* I guess we should play with sub/supertypes to find a suitable oc */
1022
	oc = backsql_name2oc( bi, &scname );
1023
1024

	if ( oc == NULL ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1025
1026
		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
			"cannot map structuralObjectClass \"%s\" -- aborting\n",
1027
			op->ora_e->e_name.bv_val,
1028
			scname.bv_val, 0 );
1029
1030
		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
		rs->sr_text = "operation not permitted within namingContext";
1031
		e = NULL;
1032
1033
1034
1035
		goto done;
	}

	if ( oc->bom_create_proc == NULL ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1036
1037
1038
		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
			"create procedure is not defined "
			"for structuralObjectClass \"%s\" - aborting\n",
1039
			op->ora_e->e_name.bv_val,
1040
			scname.bv_val, 0 );
1041
1042
		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
		rs->sr_text = "operation not permitted within namingContext";
1043
		e = NULL;
1044
1045
1046
1047
		goto done;

	} else if ( BACKSQL_CREATE_NEEDS_SELECT( bi )
			&& oc->bom_create_keyval == NULL ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1048
		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1049
			"create procedure needs select procedure, "
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1050
1051
			"but none is defined for structuralObjectClass \"%s\" "
			"- aborting\n",
1052
			op->ora_e->e_name.bv_val,
1053
			scname.bv_val, 0 );
1054
1055
		rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
		rs->sr_text = "operation not permitted within namingContext";
1056
		e = NULL;
1057
1058
1059
		goto done;
	}

1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
	/* check write access */
	if ( !access_allowed_mask( op, op->ora_e,
				slap_schema.si_ad_entry,
				NULL, ACL_WADD, NULL, &mask ) )
	{
		rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
		e = op->ora_e;
		goto done;
	}

1070
1071
	rs->sr_err = backsql_get_db_conn( op, &dbh );
	if ( rs->sr_err != LDAP_SUCCESS ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1072
		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1073
			"could not get connection handle - exiting\n", 
1074
			op->ora_e->e_name.bv_val, 0, 0 );
1075
1076
		rs->sr_text = ( rs->sr_err == LDAP_OTHER )
			?  "SQL-backend error" : NULL;
1077
		e = NULL;
1078
1079
1080
1081
1082
		goto done;
	}

	/*
	 * Check if entry exists
1083
	 *
1084
1085
	 * NOTE: backsql_api_dn2odbc() is called explicitly because
	 * we need the mucked DN to pass it to the create procedure.
1086
	 */
1087
	realdn = op->ora_e->e_name;
1088
	if ( backsql_api_dn2odbc( op, rs, &realdn ) ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1089
1090
		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
			"backsql_api_dn2odbc(\"%s\") failed\n", 
1091
			op->ora_e->e_name.bv_val, realdn.bv_val, 0 );
1092
1093
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "SQL-backend error";
1094
		e = NULL;
1095
1096
1097
		goto done;
	}

1098
	rs->sr_err = backsql_dn2id( op, rs, dbh, &realdn, NULL, 0, 0 );
1099
	if ( rs->sr_err == LDAP_SUCCESS ) {
Pierangelo Masarati's avatar
Pierangelo Masarati committed
1100
1101
		Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
			"entry exists\n",
1102
			op->ora_e->e_name.bv_val, 0, 0 );
1103
		rs->sr_err = LDAP_ALREADY_EXISTS;
1104
		e = op->ora_e;
1105
1106
1107
1108
		goto done;
	}

	/*
1109
	 * Get the parent dn and see if the corresponding entry exists.
1110
	 */
1111
	if ( be_issuffix( op->o_bd, &op->ora_e->e_nname ) ) {
1112
		pdn = slap_empty_bv;
1113

1114
	} else {
1115
		dnParent( &op->ora_e->e_nname, &pdn );
1116

1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
		/*
		 * Get the parent
		 */
		bsi.bsi_e = &p;
		rs->sr_err = backsql_init_search( &bsi, &pdn,
				LDAP_SCOPE_BASE, 
				(time_t)(-1), NULL, dbh, op, rs, slap_anlist_no_attrs,
				( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
		if ( rs->sr_err != LDAP_SUCCESS ) {
			Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
				"could not retrieve addDN parent "
				"\"%s\" ID - %s matched=\"%s\"\n", 
				pdn.bv_val,
				rs->sr_err == LDAP_REFERRAL ? "referral" : "no such entry",
				rs->sr_matched ? rs->sr_matched : "(null)" );
			e = &p;
			goto done;
		}
1135

1136
1137
1138
1139
1140
1141
1142
1143
		/* check "children" pseudo-attribute access to parent */
		if ( !access_allowed( op, &p, slap_schema.si_ad_children,
					NULL, ACL_WADD, NULL ) )
		{
			rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
			e = &p;
			goto done;
		}
1144
1145
	}

1146
1147
1148
1149
1150
1151
	/*
	 * create_proc is executed; if expect_return is set, then
	 * an output parameter is bound, which should contain 
	 * the id of the added row; otherwise the procedure
	 * is expected to return the id as the first column of a select
	 */
1152
	rc = backsql_Prepare( dbh, &sth, oc->bom_create_proc, 0 );
1153
1154
1155
	if ( rc != SQL_SUCCESS ) {
		rs->sr_err = LDAP_OTHER;
		rs->sr_text = "SQL-backend error";
1156
		e = NULL;
1157
1158
1159
		goto done;
	}

1160
	colnum = 1;
1161
	if ( BACKSQL_IS_ADD( oc->bom_expect_return ) ) {
1162
		rc = backsql_BindParamNumID( sth, 1, SQL_PARAM_OUTPUT, &new_keyval );
1163
		if ( rc != SQL_SUCCESS ) {
1164
1165
1166
1167
1168
			Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
				"error binding keyval parameter "
				"for objectClass %s\n",
				op->ora_e->e_name.bv_val,
				oc->bom_oc->soc_cname.bv_val, 0 );
1169
			backsql_PrintErrors( bi->sql_db_env, dbh, 
1170
1171
1172
1173
1174
				sth, rc );
			SQLFreeStmt( sth, SQL_DROP );

			rs->sr_text = "SQL-backend error";
			rs->sr_err = LDAP_OTHER;