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