]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/add.c
ITS#8605 - spelling fixes
[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-2017 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                 /* case SLAP_MOD_ADD_IF_NOT_PRESENT: */
393 add_only:;
394                         if ( at->bam_add_proc == NULL ) {
395                                 Debug( LDAP_DEBUG_TRACE,
396                                         "   backsql_modify_internal(): "
397                                         "add procedure is not defined "
398                                         "for attribute \"%s\"\n",
399                                         at->bam_ad->ad_cname.bv_val, 0, 0 );
400
401                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
402                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
403                                         rs->sr_text = "operation not permitted "
404                                                 "within namingContext";
405                                         goto done;
406                                 }
407
408                                 break;
409                         }
410                         
411                         Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
412                                 "adding new values for attribute \"%s\"\n",
413                                 at->bam_ad->ad_cname.bv_val, 0, 0 );
414
415                         /* can't add a NULL val array */
416                         assert( sm_values != NULL );
417                         
418                         for ( i = 0, at_val = sm_values;
419                                         !BER_BVISNULL( at_val ); 
420                                         i++, at_val++ )
421                         {
422                                 SQLHSTMT        sth = SQL_NULL_HSTMT;
423                                 /* first parameter position, parameter order */
424                                 SQLUSMALLINT    pno = 0,
425                                                 po;
426                                 /* procedure return code */
427                                 int             prc = LDAP_SUCCESS;
428
429                                 rc = backsql_Prepare( dbh, &sth, at->bam_add_proc, 0 );
430                                 if ( rc != SQL_SUCCESS ) {
431                                         Debug( LDAP_DEBUG_TRACE,
432                                                 "   backsql_modify_internal(): "
433                                                 "error preparing add query\n", 
434                                                 0, 0, 0 );
435                                         backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
436
437                                         rs->sr_err = LDAP_OTHER;
438                                         rs->sr_text = "SQL-backend error";
439                                         goto done;
440                                 }
441
442                                 if ( BACKSQL_IS_ADD( at->bam_expect_return ) ) {
443                                         pno = 1;
444                                         rc = backsql_BindParamInt( sth, 1,
445                                                 SQL_PARAM_OUTPUT, &prc );
446                                         if ( rc != SQL_SUCCESS ) {
447                                                 Debug( LDAP_DEBUG_TRACE,
448                                                         "   backsql_modify_internal(): "
449                                                         "error binding output parameter for %s[%d]\n",
450                                                         at->bam_ad->ad_cname.bv_val, i, 0 );
451                                                 backsql_PrintErrors( bi->sql_db_env, dbh, 
452                                                         sth, rc );
453                                                 SQLFreeStmt( sth, SQL_DROP );
454
455                                                 rs->sr_text = "SQL-backend error";
456                                                 rs->sr_err = LDAP_OTHER;
457                                                 goto done;
458                                         }
459                                 }
460                                 po = ( BACKSQL_IS_ADD( at->bam_param_order ) ) > 0;
461                                 rc = backsql_BindParamID( sth, pno + 1 + po,
462                                         SQL_PARAM_INPUT, &e_id->eid_keyval );
463                                 if ( rc != SQL_SUCCESS ) {
464                                         Debug( LDAP_DEBUG_TRACE,
465                                                 "   backsql_modify_internal(): "
466                                                 "error binding keyval parameter for %s[%d]\n",
467                                                 at->bam_ad->ad_cname.bv_val, i, 0 );
468                                         backsql_PrintErrors( bi->sql_db_env, dbh, 
469                                                 sth, rc );
470                                         SQLFreeStmt( sth, SQL_DROP );
471
472                                         rs->sr_text = "SQL-backend error";
473                                         rs->sr_err = LDAP_OTHER;
474                                         goto done;
475                                 }
476
477                                 Debug( LDAP_DEBUG_TRACE,
478                                         "   backsql_modify_internal(): "
479                                         "arg(%d)=" BACKSQL_IDFMT "\n", 
480                                         pno + 1 + po,
481                                         BACKSQL_IDARG(e_id->eid_keyval), 0 );
482
483                                 /*
484                                  * check for syntax needed here
485                                  * maybe need binary bind?
486                                  */
487                                 rc = backsql_BindParamBerVal( sth, pno + 2 - po,
488                                         SQL_PARAM_INPUT, at_val );
489                                 if ( rc != SQL_SUCCESS ) {
490                                         Debug( LDAP_DEBUG_TRACE,
491                                                 "   backsql_modify_internal(): "
492                                                 "error binding value parameter for %s[%d]\n",
493                                                 at->bam_ad->ad_cname.bv_val, i, 0 );
494                                         backsql_PrintErrors( bi->sql_db_env, dbh, 
495                                                 sth, rc );
496                                         SQLFreeStmt( sth, SQL_DROP );
497
498                                         rs->sr_text = "SQL-backend error";
499                                         rs->sr_err = LDAP_OTHER;
500                                         goto done;
501                                 }
502                                 Debug( LDAP_DEBUG_TRACE,
503                                         "   backsql_modify_internal(): "
504                                         "arg(%d)=\"%s\"; executing \"%s\"\n", 
505                                         pno + 2 - po, at_val->bv_val,
506                                         at->bam_add_proc );
507
508                                 rc = SQLExecute( sth );
509                                 if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
510                                         rs->sr_err = LDAP_SUCCESS;
511
512                                 } else {
513                                         Debug( LDAP_DEBUG_TRACE,
514                                                 "   backsql_modify_internal(): "
515                                                 "add_proc execution failed "
516                                                 "(rc=%d, prc=%d)\n",
517                                                 rc, prc, 0 );
518                                         if ( prc != LDAP_SUCCESS ) {
519                                                 /* SQL procedure executed fine 
520                                                  * but returned an error */
521                                                 SQLFreeStmt( sth, SQL_DROP );
522
523                                                 rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
524                                                 rs->sr_text = at->bam_ad->ad_cname.bv_val;
525                                                 return rs->sr_err;
526                                         
527                                         } else {
528                                                 backsql_PrintErrors( bi->sql_db_env, dbh,
529                                                                 sth, rc );
530                                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) 
531                                                 {
532                                                         SQLFreeStmt( sth, SQL_DROP );
533
534                                                         rs->sr_err = LDAP_OTHER;
535                                                         rs->sr_text = "SQL-backend error";
536                                                         goto done;
537                                                 }
538                                         }
539                                 }
540                                 SQLFreeStmt( sth, SQL_DROP );
541                         }
542                         break;
543                         
544                 case LDAP_MOD_DELETE:
545                 /* case SLAP_MOD_SOFTDEL: */
546                         if ( at->bam_delete_proc == NULL ) {
547                                 Debug( LDAP_DEBUG_TRACE,
548                                         "   backsql_modify_internal(): "
549                                         "delete procedure is not defined "
550                                         "for attribute \"%s\"\n",
551                                         at->bam_ad->ad_cname.bv_val, 0, 0 );
552
553                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
554                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
555                                         rs->sr_text = "operation not permitted "
556                                                 "within namingContext";
557                                         goto done;
558                                 }
559
560                                 break;
561                         }
562
563                         if ( sm_values == NULL ) {
564                                 Debug( LDAP_DEBUG_TRACE,
565                                         "   backsql_modify_internal(): "
566                                         "no values given to delete "
567                                         "for attribute \"%s\" "
568                                         "-- deleting all values\n",
569                                         at->bam_ad->ad_cname.bv_val, 0, 0 );
570                                 goto del_all;
571                         }
572
573                         Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
574                                 "deleting values for attribute \"%s\"\n",
575                                 at->bam_ad->ad_cname.bv_val, 0, 0 );
576
577                         for ( i = 0, at_val = sm_values;
578                                         !BER_BVISNULL( at_val );
579                                         i++, at_val++ )
580                         {
581                                 SQLHSTMT        sth = SQL_NULL_HSTMT;
582                                 /* first parameter position, parameter order */
583                                 SQLUSMALLINT    pno = 0,
584                                                 po;
585                                 /* procedure return code */
586                                 int             prc = LDAP_SUCCESS;
587
588                                 rc = backsql_Prepare( dbh, &sth, at->bam_delete_proc, 0 );
589                                 if ( rc != SQL_SUCCESS ) {
590                                         Debug( LDAP_DEBUG_TRACE,
591                                                 "   backsql_modify_internal(): "
592                                                 "error preparing delete query\n", 
593                                                 0, 0, 0 );
594                                         backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
595
596                                         rs->sr_err = LDAP_OTHER;
597                                         rs->sr_text = "SQL-backend error";
598                                         goto done;
599                                 }
600
601                                 if ( BACKSQL_IS_DEL( at->bam_expect_return ) ) {
602                                         pno = 1;
603                                         rc = backsql_BindParamInt( sth, 1,
604                                                 SQL_PARAM_OUTPUT, &prc );
605                                         if ( rc != SQL_SUCCESS ) {
606                                                 Debug( LDAP_DEBUG_TRACE,
607                                                         "   backsql_modify_internal(): "
608                                                         "error binding output parameter for %s[%d]\n",
609                                                         at->bam_ad->ad_cname.bv_val, i, 0 );
610                                                 backsql_PrintErrors( bi->sql_db_env, dbh, 
611                                                         sth, rc );
612                                                 SQLFreeStmt( sth, SQL_DROP );
613
614                                                 rs->sr_text = "SQL-backend error";
615                                                 rs->sr_err = LDAP_OTHER;
616                                                 goto done;
617                                         }
618                                 }
619                                 po = ( BACKSQL_IS_DEL( at->bam_param_order ) ) > 0;
620                                 rc = backsql_BindParamID( sth, pno + 1 + po,
621                                         SQL_PARAM_INPUT, &e_id->eid_keyval );
622                                 if ( rc != SQL_SUCCESS ) {
623                                         Debug( LDAP_DEBUG_TRACE,
624                                                 "   backsql_modify_internal(): "
625                                                 "error binding keyval parameter for %s[%d]\n",
626                                                 at->bam_ad->ad_cname.bv_val, i, 0 );
627                                         backsql_PrintErrors( bi->sql_db_env, dbh, 
628                                                 sth, rc );
629                                         SQLFreeStmt( sth, SQL_DROP );
630
631                                         rs->sr_text = "SQL-backend error";
632                                         rs->sr_err = LDAP_OTHER;
633                                         goto done;
634                                 }
635
636                                 Debug( LDAP_DEBUG_TRACE,
637                                         "   backsql_modify_internal(): "
638                                         "arg(%d)=" BACKSQL_IDFMT "\n", 
639                                         pno + 1 + po,
640                                         BACKSQL_IDARG(e_id->eid_keyval), 0 );
641
642                                 /*
643                                  * check for syntax needed here 
644                                  * maybe need binary bind?
645                                  */
646                                 rc = backsql_BindParamBerVal( sth, pno + 2 - po,
647                                         SQL_PARAM_INPUT, at_val );
648                                 if ( rc != SQL_SUCCESS ) {
649                                         Debug( LDAP_DEBUG_TRACE,
650                                                 "   backsql_modify_internal(): "
651                                                 "error binding value parameter for %s[%d]\n",
652                                                 at->bam_ad->ad_cname.bv_val, i, 0 );
653                                         backsql_PrintErrors( bi->sql_db_env, dbh, 
654                                                 sth, rc );
655                                         SQLFreeStmt( sth, SQL_DROP );
656
657                                         rs->sr_text = "SQL-backend error";
658                                         rs->sr_err = LDAP_OTHER;
659                                         goto done;
660                                 }
661
662                                 Debug( LDAP_DEBUG_TRACE,
663                                         "   backsql_modify_internal(): "
664                                         "executing \"%s\"\n", 
665                                         at->bam_delete_proc, 0, 0 );
666                                 rc = SQLExecute( sth );
667                                 if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS )
668                                 {
669                                         rs->sr_err = LDAP_SUCCESS;
670                                         
671                                 } else {
672                                         Debug( LDAP_DEBUG_TRACE,
673                                                 "   backsql_modify_internal(): "
674                                                 "delete_proc execution "
675                                                 "failed (rc=%d, prc=%d)\n",
676                                                 rc, prc, 0 );
677
678                                         if ( prc != LDAP_SUCCESS ) {
679                                                 /* SQL procedure executed fine
680                                                  * but returned an error */
681                                                 rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
682                                                 rs->sr_text = at->bam_ad->ad_cname.bv_val;
683                                                 goto done;
684                                                 
685                                         } else {
686                                                 backsql_PrintErrors( bi->sql_db_env,
687                                                                 dbh, sth, rc );
688                                                 SQLFreeStmt( sth, SQL_DROP );
689                                                 rs->sr_err = LDAP_OTHER;
690                                                 rs->sr_text = at->bam_ad->ad_cname.bv_val;
691                                                 goto done;
692                                         }
693                                 }
694                                 SQLFreeStmt( sth, SQL_DROP );
695                         }
696                         break;
697
698                 case LDAP_MOD_INCREMENT:
699                         Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
700                                 "increment not supported yet\n", 0, 0, 0 );
701                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
702                                 rs->sr_err = LDAP_OTHER;
703                                 rs->sr_text = "SQL-backend error";
704                                 goto done;
705                         }
706                         break;
707                 }
708         }
709
710 done:;
711         Debug( LDAP_DEBUG_TRACE, "<==backsql_modify_internal(): %d%s%s\n",
712                 rs->sr_err,
713                 rs->sr_text ? ": " : "",
714                 rs->sr_text ? rs->sr_text : "" );
715
716         /*
717          * FIXME: should fail in case one change fails?
718          */
719         return rs->sr_err;
720 }
721
722 static int
723 backsql_add_attr(
724         Operation               *op,
725         SlapReply               *rs,
726         SQLHDBC                 dbh,
727         backsql_oc_map_rec      *oc,
728         Attribute               *at,
729         backsql_key_t           new_keyval )
730 {
731         backsql_info            *bi = (backsql_info*)op->o_bd->be_private;
732         backsql_at_map_rec      *at_rec = NULL;
733         struct berval           *at_val;
734         unsigned long           i;
735         RETCODE                 rc;
736         SQLUSMALLINT            currpos;
737         SQLHSTMT                sth = SQL_NULL_HSTMT;
738
739         at_rec = backsql_ad2at( oc, at->a_desc ); 
740   
741         if ( at_rec == NULL ) {
742                 Debug( LDAP_DEBUG_TRACE, "   backsql_add_attr(\"%s\"): "
743                         "attribute \"%s\" is not registered "
744                         "in objectclass \"%s\"\n",
745                         op->ora_e->e_name.bv_val,
746                         at->a_desc->ad_cname.bv_val,
747                         BACKSQL_OC_NAME( oc ) );
748
749                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
750                         rs->sr_text = "operation not permitted "
751                                 "within namingContext";
752                         return rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
753                 }
754
755                 return LDAP_SUCCESS;
756         }
757         
758         if ( at_rec->bam_add_proc == NULL ) {
759                 Debug( LDAP_DEBUG_TRACE, "   backsql_add_attr(\"%s\"): "
760                         "add procedure is not defined "
761                         "for attribute \"%s\" "
762                         "of structuralObjectClass \"%s\"\n",
763                         op->ora_e->e_name.bv_val,
764                         at->a_desc->ad_cname.bv_val,
765                         BACKSQL_OC_NAME( oc ) );
766
767                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
768                         rs->sr_text = "operation not permitted "
769                                 "within namingContext";
770                         return rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
771                 }
772
773                 return LDAP_SUCCESS;
774         }
775
776         for ( i = 0, at_val = &at->a_vals[ i ];
777                         !BER_BVISNULL( at_val );
778                         i++, at_val = &at->a_vals[ i ] )
779         {
780                 /* procedure return code */
781                 int             prc = LDAP_SUCCESS;
782                 /* first parameter #, parameter order */
783                 SQLUSMALLINT    pno, po;
784                 char            logbuf[ STRLENOF("val[], id=") + 2*LDAP_PVT_INTTYPE_CHARS(unsigned long)];
785                 
786                 /*
787                  * Do not deal with the objectClass that is used
788                  * to build the entry
789                  */
790                 if ( at->a_desc == slap_schema.si_ad_objectClass ) {
791                         if ( dn_match( at_val, &oc->bom_oc->soc_cname ) )
792                         {
793                                 continue;
794                         }
795                 }
796
797                 rc = backsql_Prepare( dbh, &sth, at_rec->bam_add_proc, 0 );
798                 if ( rc != SQL_SUCCESS ) {
799                         rs->sr_text = "SQL-backend error";
800                         return rs->sr_err = LDAP_OTHER;
801                 }
802
803                 if ( BACKSQL_IS_ADD( at_rec->bam_expect_return ) ) {
804                         pno = 1;
805                         rc = backsql_BindParamInt( sth, 1, SQL_PARAM_OUTPUT, &prc );
806                         if ( rc != SQL_SUCCESS ) {
807                                 Debug( LDAP_DEBUG_TRACE,
808                                         "   backsql_add_attr(): "
809                                         "error binding output parameter for %s[%lu]\n",
810                                         at_rec->bam_ad->ad_cname.bv_val, i, 0 );
811                                 backsql_PrintErrors( bi->sql_db_env, dbh, 
812                                         sth, rc );
813                                 SQLFreeStmt( sth, SQL_DROP );
814
815                                 rs->sr_text = "SQL-backend error";
816                                 return rs->sr_err = LDAP_OTHER;
817                         }
818
819                 } else {
820                         pno = 0;
821                 }
822
823                 po = ( BACKSQL_IS_ADD( at_rec->bam_param_order ) ) > 0;
824                 currpos = pno + 1 + po;
825                 rc = backsql_BindParamNumID( sth, currpos,
826                                 SQL_PARAM_INPUT, &new_keyval );
827                 if ( rc != SQL_SUCCESS ) {
828                         Debug( LDAP_DEBUG_TRACE,
829                                 "   backsql_add_attr(): "
830                                 "error binding keyval parameter for %s[%lu]\n",
831                                 at_rec->bam_ad->ad_cname.bv_val, i, 0 );
832                         backsql_PrintErrors( bi->sql_db_env, dbh, 
833                                 sth, rc );
834                         SQLFreeStmt( sth, SQL_DROP );
835
836                         rs->sr_text = "SQL-backend error";
837                         return rs->sr_err = LDAP_OTHER;
838                 }
839
840                 currpos = pno + 2 - po;
841
842                 /*
843                  * check for syntax needed here 
844                  * maybe need binary bind?
845                  */
846
847                 rc = backsql_BindParamBerVal( sth, currpos, SQL_PARAM_INPUT, at_val );
848                 if ( rc != SQL_SUCCESS ) {
849                         Debug( LDAP_DEBUG_TRACE,
850                                 "   backsql_add_attr(): "
851                                 "error binding value parameter for %s[%lu]\n",
852                                 at_rec->bam_ad->ad_cname.bv_val, i, 0 );
853                         backsql_PrintErrors( bi->sql_db_env, dbh, 
854                                 sth, rc );
855                         SQLFreeStmt( sth, SQL_DROP );
856
857                         rs->sr_text = "SQL-backend error";
858                         return rs->sr_err = LDAP_OTHER;
859                 }
860
861 #ifdef LDAP_DEBUG
862                 if ( LogTest( LDAP_DEBUG_TRACE ) ) {
863                         snprintf( logbuf, sizeof( logbuf ), "val[%lu], id=" BACKSQL_IDNUMFMT,
864                                         i, new_keyval );
865                         Debug( LDAP_DEBUG_TRACE, "   backsql_add_attr(\"%s\"): "
866                                 "executing \"%s\" %s\n", 
867                                 op->ora_e->e_name.bv_val,
868                                 at_rec->bam_add_proc, logbuf );
869                 }
870 #endif
871                 rc = SQLExecute( sth );
872                 if ( rc == SQL_SUCCESS && prc == LDAP_SUCCESS ) {
873                         rs->sr_err = LDAP_SUCCESS;
874
875                 } 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                         if ( prc != LDAP_SUCCESS ) {
881                                 /* SQL procedure executed fine
882                                  * but returned an error */
883                                 rs->sr_err = BACKSQL_SANITIZE_ERROR( prc );
884                                 rs->sr_text = op->ora_e->e_name.bv_val;
885                                 SQLFreeStmt( sth, SQL_DROP );
886                                 return rs->sr_err;
887
888                         } else {
889                                 backsql_PrintErrors( bi->sql_db_env, dbh,
890                                                 sth, rc );
891                                 rs->sr_err = LDAP_OTHER;
892                                 rs->sr_text = op->ora_e->e_name.bv_val;
893                                 SQLFreeStmt( sth, SQL_DROP );
894                                 return rs->sr_err;
895                         }
896                 }
897                 SQLFreeStmt( sth, SQL_DROP );
898         }
899
900         return LDAP_SUCCESS;
901 }
902
903 int
904 backsql_add( Operation *op, SlapReply *rs )
905 {
906         backsql_info            *bi = (backsql_info*)op->o_bd->be_private;
907         SQLHDBC                 dbh = SQL_NULL_HDBC;
908         SQLHSTMT                sth = SQL_NULL_HSTMT;
909         backsql_key_t           new_keyval = 0;
910         RETCODE                 rc;
911         backsql_oc_map_rec      *oc = NULL;
912         backsql_srch_info       bsi = { 0 };
913         Entry                   p = { 0 }, *e = NULL;
914         Attribute               *at,
915                                 *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         char                    textbuf[ SLAP_TEXT_BUFLEN ];
924         size_t                  textlen = sizeof( textbuf );
925
926 #ifdef BACKSQL_SYNCPROV
927         /*
928          * NOTE: fake successful result to force contextCSN to be bumped up
929          */
930         if ( op->o_sync ) {
931                 char            buf[ LDAP_PVT_CSNSTR_BUFSIZE ];
932                 struct berval   csn;
933
934                 csn.bv_val = buf;
935                 csn.bv_len = sizeof( buf );
936                 slap_get_csn( op, &csn, 1 );
937
938                 rs->sr_err = LDAP_SUCCESS;
939                 send_ldap_result( op, rs );
940
941                 slap_graduate_commit_csn( op );
942
943                 return 0;
944         }
945 #endif /* BACKSQL_SYNCPROV */
946
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                 char            textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };
953
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 ) {
957                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
958                                 "entry failed schema check -- aborting\n",
959                                 op->ora_e->e_name.bv_val, 0, 0 );
960                         e = NULL;
961                         goto done;
962                 }
963         }
964
965         slap_add_opattrs( op, &rs->sr_text, textbuf, textlen, 1 );
966
967         if ( get_assert( op ) &&
968                 ( test_filter( op, op->ora_e, get_assertion( op )) != LDAP_COMPARE_TRUE ))
969         {
970                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
971                         "assertion control failed -- aborting\n",
972                         op->ora_e->e_name.bv_val, 0, 0 );
973                 e = NULL;
974                 rs->sr_err = LDAP_ASSERTION_FAILED;
975                 goto done;
976         }
977
978         /* search structuralObjectClass */
979         for ( at = op->ora_e->e_attrs; at != NULL; at = at->a_next ) {
980                 if ( at->a_desc == slap_schema.si_ad_structuralObjectClass ) {
981                         break;
982                 }
983         }
984
985         /* there must exist */
986         if ( at == NULL ) {
987                 char            buf[ SLAP_TEXT_BUFLEN ];
988                 const char      *text;
989
990                 /* search structuralObjectClass */
991                 for ( at = op->ora_e->e_attrs; at != NULL; at = at->a_next ) {
992                         if ( at->a_desc == slap_schema.si_ad_objectClass ) {
993                                 break;
994                         }
995                 }
996
997                 if ( at == NULL ) {
998                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
999                                 "no objectClass\n",
1000                                 op->ora_e->e_name.bv_val, 0, 0 );
1001                         rs->sr_err = LDAP_OBJECT_CLASS_VIOLATION;
1002                         e = NULL;
1003                         goto done;
1004                 }
1005
1006                 rs->sr_err = structural_class( at->a_vals, &soc, NULL,
1007                                 &text, buf, sizeof( buf ), op->o_tmpmemctx );
1008                 if ( rs->sr_err != LDAP_SUCCESS ) {
1009                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1010                                 "%s (%d)\n",
1011                                 op->ora_e->e_name.bv_val, text, rs->sr_err );
1012                         e = NULL;
1013                         goto done;
1014                 }
1015                 scname = soc->soc_cname;
1016
1017         } else {
1018                 scname = at->a_vals[0];
1019         }
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 ) {
1025                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1026                         "cannot map structuralObjectClass \"%s\" -- aborting\n",
1027                         op->ora_e->e_name.bv_val,
1028                         scname.bv_val, 0 );
1029                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1030                 rs->sr_text = "operation not permitted within namingContext";
1031                 e = NULL;
1032                 goto done;
1033         }
1034
1035         if ( oc->bom_create_proc == NULL ) {
1036                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1037                         "create procedure is not defined "
1038                         "for structuralObjectClass \"%s\" - aborting\n",
1039                         op->ora_e->e_name.bv_val,
1040                         scname.bv_val, 0 );
1041                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1042                 rs->sr_text = "operation not permitted within namingContext";
1043                 e = NULL;
1044                 goto done;
1045
1046         } else if ( BACKSQL_CREATE_NEEDS_SELECT( bi )
1047                         && oc->bom_create_keyval == NULL ) {
1048                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1049                         "create procedure needs select procedure, "
1050                         "but none is defined for structuralObjectClass \"%s\" "
1051                         "- aborting\n",
1052                         op->ora_e->e_name.bv_val,
1053                         scname.bv_val, 0 );
1054                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1055                 rs->sr_text = "operation not permitted within namingContext";
1056                 e = NULL;
1057                 goto done;
1058         }
1059
1060         /* check write access */
1061         if ( !access_allowed_mask( op, op->ora_e,
1062                                 slap_schema.si_ad_entry,
1063                                 NULL, ACL_WADD, NULL, &mask ) )
1064         {
1065                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1066                 e = op->ora_e;
1067                 goto done;
1068         }
1069
1070         rs->sr_err = backsql_get_db_conn( op, &dbh );
1071         if ( rs->sr_err != LDAP_SUCCESS ) {
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                 rs->sr_text = ( rs->sr_err == LDAP_OTHER )
1076                         ?  "SQL-backend error" : NULL;
1077                 e = NULL;
1078                 goto done;
1079         }
1080
1081         /*
1082          * Check if entry exists
1083          *
1084          * NOTE: backsql_api_dn2odbc() is called explicitly because
1085          * 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 ) ) {
1089                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1090                         "backsql_api_dn2odbc(\"%s\") failed\n", 
1091                         op->ora_e->e_name.bv_val, realdn.bv_val, 0 );
1092                 rs->sr_err = LDAP_OTHER;
1093                 rs->sr_text = "SQL-backend error";
1094                 e = NULL;
1095                 goto done;
1096         }
1097
1098         rs->sr_err = backsql_dn2id( op, rs, dbh, &realdn, NULL, 0, 0 );
1099         if ( rs->sr_err == LDAP_SUCCESS ) {
1100                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1101                         "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                 goto done;
1106         }
1107
1108         /*
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                  * Get the parent
1119                  */
1120                 bsi.bsi_e = &p;
1121                 rs->sr_err = backsql_init_search( &bsi, &pdn,
1122                                 LDAP_SCOPE_BASE, 
1123                                 (time_t)(-1), NULL, dbh, op, rs, slap_anlist_no_attrs,
1124                                 ( BACKSQL_ISF_MATCHED | BACKSQL_ISF_GET_ENTRY ) );
1125                 if ( rs->sr_err != LDAP_SUCCESS ) {
1126                         Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1127                                 "could not retrieve addDN parent "
1128                                 "\"%s\" ID - %s matched=\"%s\"\n", 
1129                                 pdn.bv_val,
1130                                 rs->sr_err == LDAP_REFERRAL ? "referral" : "no such entry",
1131                                 rs->sr_matched ? rs->sr_matched : "(null)" );
1132                         e = &p;
1133                         goto done;
1134                 }
1135
1136                 /* check "children" pseudo-attribute access to parent */
1137                 if ( !access_allowed( op, &p, slap_schema.si_ad_children,
1138                                         NULL, ACL_WADD, NULL ) )
1139                 {
1140                         rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1141                         e = &p;
1142                         goto done;
1143                 }
1144         }
1145
1146         /*
1147          * create_proc is executed; if expect_return is set, then
1148          * an output parameter is bound, which should contain 
1149          * the id of the added row; otherwise the procedure
1150          * is expected to return the id as the first column of a select
1151          */
1152         rc = backsql_Prepare( dbh, &sth, oc->bom_create_proc, 0 );
1153         if ( rc != SQL_SUCCESS ) {
1154                 rs->sr_err = LDAP_OTHER;
1155                 rs->sr_text = "SQL-backend error";
1156                 e = NULL;
1157                 goto done;
1158         }
1159
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                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1165                                 "error binding keyval parameter "
1166                                 "for objectClass %s\n",
1167                                 op->ora_e->e_name.bv_val,
1168                                 oc->bom_oc->soc_cname.bv_val, 0 );
1169                         backsql_PrintErrors( bi->sql_db_env, dbh, 
1170                                 sth, rc );
1171                         SQLFreeStmt( sth, SQL_DROP );
1172
1173                         rs->sr_text = "SQL-backend error";
1174                         rs->sr_err = LDAP_OTHER;
1175                         e = NULL;
1176                         goto done;
1177                 }
1178                 colnum++;
1179         }
1180
1181         if ( oc->bom_create_hint ) {
1182                 at = attr_find( op->ora_e->e_attrs, oc->bom_create_hint );
1183                 if ( at && at->a_vals ) {
1184                         backsql_BindParamStr( sth, colnum, SQL_PARAM_INPUT,
1185                                         at->a_vals[0].bv_val,
1186                                         at->a_vals[0].bv_len );
1187                         Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1188                                         "create_proc hint: param = '%s'\n",
1189                                         at->a_vals[0].bv_val, 0, 0 );
1190
1191                 } else {
1192                         backsql_BindParamStr( sth, colnum, SQL_PARAM_INPUT,
1193                                         "", 0 );
1194                         Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1195                                         "create_proc hint (%s) not available\n",
1196                                         oc->bom_create_hint->ad_cname.bv_val,
1197                                         0, 0 );
1198                 }
1199                 colnum++;
1200         }
1201
1202         Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): executing \"%s\"\n",
1203                 op->ora_e->e_name.bv_val, oc->bom_create_proc, 0 );
1204         rc = SQLExecute( sth );
1205         if ( rc != SQL_SUCCESS ) {
1206                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1207                         "create_proc execution failed\n",
1208                         op->ora_e->e_name.bv_val, 0, 0 );
1209                 backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc);
1210                 SQLFreeStmt( sth, SQL_DROP );
1211                 rs->sr_err = LDAP_OTHER;
1212                 rs->sr_text = "SQL-backend error";
1213                 e = NULL;
1214                 goto done;
1215         }
1216
1217         /* FIXME: after SQLExecute(), the row is already inserted
1218          * (at least with PostgreSQL and unixODBC); needs investigation */
1219
1220         if ( !BACKSQL_IS_ADD( oc->bom_expect_return ) ) {
1221                 SWORD           ncols;
1222                 SQLLEN          value_len;
1223
1224                 if ( BACKSQL_CREATE_NEEDS_SELECT( bi ) ) {
1225                         SQLFreeStmt( sth, SQL_DROP );
1226
1227                         rc = backsql_Prepare( dbh, &sth, oc->bom_create_keyval, 0 );
1228                         if ( rc != SQL_SUCCESS ) {
1229                                 rs->sr_err = LDAP_OTHER;
1230                                 rs->sr_text = "SQL-backend error";
1231                                 e = NULL;
1232                                 goto done;
1233                         }
1234
1235                         rc = SQLExecute( sth );
1236                         if ( rc != SQL_SUCCESS ) {
1237                                 rs->sr_err = LDAP_OTHER;
1238                                 rs->sr_text = "SQL-backend error";
1239                                 e = NULL;
1240                                 goto done;
1241                         }
1242                 }
1243
1244                 /*
1245                  * the query to know the id of the inserted entry
1246                  * must be embedded in the create procedure
1247                  */
1248                 rc = SQLNumResultCols( sth, &ncols );
1249                 if ( rc != SQL_SUCCESS ) {
1250                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1251                                 "create_proc result evaluation failed\n",
1252                                 op->ora_e->e_name.bv_val, 0, 0 );
1253                         backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc);
1254                         SQLFreeStmt( sth, SQL_DROP );
1255                         rs->sr_err = LDAP_OTHER;
1256                         rs->sr_text = "SQL-backend error";
1257                         e = NULL;
1258                         goto done;
1259
1260                 } else if ( ncols != 1 ) {
1261                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1262                                 "create_proc result is bogus (ncols=%d)\n",
1263                                 op->ora_e->e_name.bv_val, ncols, 0 );
1264                         backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc);
1265                         SQLFreeStmt( sth, SQL_DROP );
1266                         rs->sr_err = LDAP_OTHER;
1267                         rs->sr_text = "SQL-backend error";
1268                         e = NULL;
1269                         goto done;
1270                 }
1271
1272 #if 0
1273                 {
1274                         SQLCHAR         colname[ 64 ];
1275                         SQLSMALLINT     name_len, col_type, col_scale, col_null;
1276                         UDWORD          col_prec;
1277
1278                         /*
1279                          * FIXME: check whether col_type is compatible,
1280                          * if it can be null and so on ...
1281                          */
1282                         rc = SQLDescribeCol( sth, (SQLUSMALLINT)1, 
1283                                         &colname[ 0 ], 
1284                                         (SQLUINTEGER)( sizeof( colname ) - 1 ),
1285                                         &name_len, &col_type,
1286                                         &col_prec, &col_scale, &col_null );
1287                 }
1288 #endif
1289
1290                 rc = SQLBindCol( sth, (SQLUSMALLINT)1, SQL_C_ULONG,
1291                                 (SQLPOINTER)&new_keyval, 
1292                                 (SQLINTEGER)sizeof( new_keyval ), 
1293                                 &value_len );
1294
1295                 rc = SQLFetch( sth );
1296
1297                 if ( value_len <= 0 ) {
1298                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1299                                 "create_proc result is empty?\n",
1300                                 op->ora_e->e_name.bv_val, 0, 0 );
1301                         backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc);
1302                         SQLFreeStmt( sth, SQL_DROP );
1303                         rs->sr_err = LDAP_OTHER;
1304                         rs->sr_text = "SQL-backend error";
1305                         e = NULL;
1306                         goto done;
1307                 }
1308         }
1309
1310         SQLFreeStmt( sth, SQL_DROP );
1311
1312         Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1313                 "create_proc returned keyval=" BACKSQL_IDNUMFMT "\n",
1314                 op->ora_e->e_name.bv_val, new_keyval, 0 );
1315
1316         rc = backsql_Prepare( dbh, &sth, bi->sql_insentry_stmt, 0 );
1317         if ( rc != SQL_SUCCESS ) {
1318                 rs->sr_err = LDAP_OTHER;
1319                 rs->sr_text = "SQL-backend error";
1320                 e = NULL;
1321                 goto done;
1322         }
1323         
1324         rc = backsql_BindParamBerVal( sth, 1, SQL_PARAM_INPUT, &realdn );
1325         if ( rc != SQL_SUCCESS ) {
1326                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1327                         "error binding DN parameter for objectClass %s\n",
1328                         op->ora_e->e_name.bv_val,
1329                         oc->bom_oc->soc_cname.bv_val, 0 );
1330                 backsql_PrintErrors( bi->sql_db_env, dbh, 
1331                         sth, rc );
1332                 SQLFreeStmt( sth, SQL_DROP );
1333
1334                 rs->sr_text = "SQL-backend error";
1335                 rs->sr_err = LDAP_OTHER;
1336                 e = NULL;
1337                 goto done;
1338         }
1339
1340         rc = backsql_BindParamNumID( sth, 2, SQL_PARAM_INPUT, &oc->bom_id );
1341         if ( rc != SQL_SUCCESS ) {
1342                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1343                         "error binding objectClass ID parameter "
1344                         "for objectClass %s\n",
1345                         op->ora_e->e_name.bv_val,
1346                         oc->bom_oc->soc_cname.bv_val, 0 );
1347                 backsql_PrintErrors( bi->sql_db_env, dbh, 
1348                         sth, rc );
1349                 SQLFreeStmt( sth, SQL_DROP );
1350
1351                 rs->sr_text = "SQL-backend error";
1352                 rs->sr_err = LDAP_OTHER;
1353                 e = NULL;
1354                 goto done;
1355         }
1356
1357         rc = backsql_BindParamID( sth, 3, SQL_PARAM_INPUT, &bsi.bsi_base_id.eid_id );
1358         if ( rc != SQL_SUCCESS ) {
1359                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1360                         "error binding parent ID parameter "
1361                         "for objectClass %s\n",
1362                         op->ora_e->e_name.bv_val,
1363                         oc->bom_oc->soc_cname.bv_val, 0 );
1364                 backsql_PrintErrors( bi->sql_db_env, dbh, 
1365                         sth, rc );
1366                 SQLFreeStmt( sth, SQL_DROP );
1367
1368                 rs->sr_text = "SQL-backend error";
1369                 rs->sr_err = LDAP_OTHER;
1370                 e = NULL;
1371                 goto done;
1372         }
1373
1374         rc = backsql_BindParamNumID( sth, 4, SQL_PARAM_INPUT, &new_keyval );
1375         if ( rc != SQL_SUCCESS ) {
1376                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1377                         "error binding entry ID parameter "
1378                         "for objectClass %s\n",
1379                         op->ora_e->e_name.bv_val,
1380                         oc->bom_oc->soc_cname.bv_val, 0 );
1381                 backsql_PrintErrors( bi->sql_db_env, dbh, 
1382                         sth, rc );
1383                 SQLFreeStmt( sth, SQL_DROP );
1384
1385                 rs->sr_text = "SQL-backend error";
1386                 rs->sr_err = LDAP_OTHER;
1387                 e = NULL;
1388                 goto done;
1389         }
1390
1391         if ( LogTest( LDAP_DEBUG_TRACE ) ) {
1392                 char buf[ SLAP_TEXT_BUFLEN ];
1393
1394                 snprintf( buf, sizeof(buf),
1395                         "executing \"%s\" for dn=\"%s\"  oc_map_id=" BACKSQL_IDNUMFMT " p_id=" BACKSQL_IDFMT " keyval=" BACKSQL_IDNUMFMT,
1396                         bi->sql_insentry_stmt, op->ora_e->e_name.bv_val,
1397                         oc->bom_id, BACKSQL_IDARG(bsi.bsi_base_id.eid_id),
1398                         new_keyval );
1399                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): %s\n", buf, 0, 0 );
1400         }
1401
1402         rc = SQLExecute( sth );
1403         if ( rc != SQL_SUCCESS ) {
1404                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(\"%s\"): "
1405                         "could not insert ldap_entries record\n",
1406                         op->ora_e->e_name.bv_val, 0, 0 );
1407                 backsql_PrintErrors( bi->sql_db_env, dbh, sth, rc );
1408                 
1409                 /*
1410                  * execute delete_proc to delete data added !!!
1411                  */
1412                 SQLFreeStmt( sth, SQL_DROP );
1413                 rs->sr_err = LDAP_OTHER;
1414                 rs->sr_text = "SQL-backend error";
1415                 e = NULL;
1416                 goto done;
1417         }
1418
1419         SQLFreeStmt( sth, SQL_DROP );
1420
1421         for ( at = op->ora_e->e_attrs; at != NULL; at = at->a_next ) {
1422                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
1423                         "adding attribute \"%s\"\n", 
1424                         at->a_desc->ad_cname.bv_val, 0, 0 );
1425
1426                 /*
1427                  * Skip:
1428                  * - the first occurrence of objectClass, which is used
1429                  *   to determine how to build the SQL entry (FIXME ?!?)
1430                  * - operational attributes
1431                  * - empty attributes (FIXME ?!?)
1432                  */
1433                 if ( backsql_attr_skip( at->a_desc, at->a_vals ) ) {
1434                         continue;
1435                 }
1436
1437                 if ( at->a_desc == slap_schema.si_ad_objectClass ) {
1438                         at_objectClass = at;
1439                         continue;
1440                 }
1441
1442                 rs->sr_err = backsql_add_attr( op, rs, dbh, oc, at, new_keyval );
1443                 if ( rs->sr_err != LDAP_SUCCESS ) {
1444                         e = op->ora_e;
1445                         goto done;
1446                 }
1447         }
1448
1449         if ( at_objectClass ) {
1450                 rs->sr_err = backsql_add_attr( op, rs, dbh, oc,
1451                                 at_objectClass, new_keyval );
1452                 if ( rs->sr_err != LDAP_SUCCESS ) {
1453                         e = op->ora_e;
1454                         goto done;
1455                 }
1456         }
1457
1458 done:;
1459         /*
1460          * Commit only if all operations succeed
1461          */
1462         if ( sth != SQL_NULL_HSTMT ) {
1463                 SQLUSMALLINT    CompletionType = SQL_ROLLBACK;
1464
1465                 if ( rs->sr_err == LDAP_SUCCESS && !op->o_noop ) {
1466                         assert( e == NULL );
1467                         CompletionType = SQL_COMMIT;
1468                 }
1469
1470                 SQLTransact( SQL_NULL_HENV, dbh, CompletionType );
1471         }
1472
1473         /*
1474          * FIXME: NOOP does not work for add -- it works for all 
1475          * the other operations, and I don't get the reason :(
1476          * 
1477          * hint: there might be some autocommit in Postgres
1478          * so that when the unique id of the key table is
1479          * automatically increased, there's no rollback.
1480          * We might implement a "rollback" procedure consisting
1481          * in deleting that row.
1482          */
1483
1484         if ( e != NULL ) {
1485                 int     disclose = 1;
1486
1487                 if ( e == op->ora_e && !ACL_GRANT( mask, ACL_DISCLOSE ) ) {
1488                         /* mask already collected */
1489                         disclose = 0;
1490
1491                 } else if ( e == &p && !access_allowed( op, &p,
1492                                         slap_schema.si_ad_entry, NULL,
1493                                         ACL_DISCLOSE, NULL ) )
1494                 {
1495                         disclose = 0;
1496                 }
1497
1498                 if ( disclose == 0 ) {
1499                         rs->sr_err = LDAP_NO_SUCH_OBJECT;
1500                         rs->sr_text = NULL;
1501                         rs->sr_matched = NULL;
1502                         if ( rs->sr_ref ) {
1503                                 ber_bvarray_free( rs->sr_ref );
1504                                 rs->sr_ref = NULL;
1505                         }
1506                 }
1507         }
1508
1509         if ( op->o_noop && rs->sr_err == LDAP_SUCCESS ) {
1510                 rs->sr_err = LDAP_X_NO_OPERATION;
1511         }
1512
1513         send_ldap_result( op, rs );
1514         slap_graduate_commit_csn( op );
1515
1516         if ( !BER_BVISNULL( &realdn )
1517                         && realdn.bv_val != op->ora_e->e_name.bv_val )
1518         {
1519                 ch_free( realdn.bv_val );
1520         }
1521
1522         if ( !BER_BVISNULL( &bsi.bsi_base_id.eid_ndn ) ) {
1523                 (void)backsql_free_entryID( &bsi.bsi_base_id, 0, op->o_tmpmemctx );
1524         }
1525
1526         if ( !BER_BVISNULL( &p.e_nname ) ) {
1527                 backsql_entry_clean( op, &p );
1528         }
1529
1530         Debug( LDAP_DEBUG_TRACE, "<==backsql_add(\"%s\"): %d \"%s\"\n",
1531                         op->ora_e->e_name.bv_val,
1532                         rs->sr_err,
1533                         rs->sr_text ? rs->sr_text : "" );
1534
1535         rs->sr_text = NULL;
1536         rs->sr_matched = NULL;
1537         if ( rs->sr_ref ) {
1538                 ber_bvarray_free( rs->sr_ref );
1539                 rs->sr_ref = NULL;
1540         }
1541
1542         return rs->sr_err;
1543 }
1544