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