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