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