]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/modify.c
fix update operations
[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 );
683         rs->sr_err = dnNormalize2( NULL, &new_dn, &new_ndn, op->o_tmpmemctx );
684         if ( rs->sr_err != LDAP_SUCCESS ) {
685                 Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
686                         "new dn is invalid ('%s') - aborting\n",
687                         new_dn.bv_val, 0, 0 );
688                 rs->sr_text = "unable to build new DN";
689                 send_ldap_result( op, rs );
690                 goto modrdn_return;
691         }
692         
693         Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): new entry dn is '%s'\n",
694                         new_dn.bv_val, 0, 0 );
695
696         rs->sr_err = backsql_dn2id( bi, &pe_id, dbh, &p_ndn );
697         if ( rs->sr_err != LDAP_SUCCESS ) {
698                 Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
699                         "could not lookup old parent entry id\n", 0, 0, 0 );
700                 rs->sr_text = ( rs->sr_err == LDAP_OTHER )
701                         ? "SQL-backend error" : NULL;
702                 send_ldap_result( op, rs );
703                 goto modrdn_return;
704         }
705
706         Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
707                 "old parent entry id is %ld\n", pe_id.id, 0, 0 );
708
709         rs->sr_err = backsql_dn2id( bi, &new_pid, dbh, new_npdn );
710         if ( rs->sr_err != LDAP_SUCCESS ) {
711                 Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
712                         "could not lookup new parent entry id\n", 0, 0, 0 );
713                 rs->sr_text = ( rs->sr_err == LDAP_OTHER )
714                         ? "SQL-backend error" : NULL;
715                 send_ldap_result( op, rs );
716                 goto modrdn_return;
717         }
718         
719         Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
720                 "new parent entry id is %ld\n", new_pid.id, 0, 0 );
721
722  
723         Debug(  LDAP_DEBUG_TRACE, "backsql_modrdn(): "
724                 "executing delentry_query\n", 0, 0, 0 );
725         SQLAllocStmt( dbh, &sth );
726         SQLBindParameter( sth, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,
727                         0, 0, &e_id.id, 0, 0 );
728         rc = SQLExecDirect( sth, bi->delentry_query, SQL_NTS );
729         if ( rc != SQL_SUCCESS ) {
730                 Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
731                         "failed to delete record from ldap_entries\n",
732                         0, 0, 0 );
733                 backsql_PrintErrors( bi->db_env, dbh, sth, rc );
734                 rs->sr_err = LDAP_OTHER;
735                 rs->sr_text = "SQL-backend error";
736                 send_ldap_result( op, rs );
737                 goto modrdn_return;
738         }
739
740         SQLFreeStmt( sth, SQL_RESET_PARAMS );
741
742         Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
743                 "executing insentry_query\n", 0, 0, 0 );
744         backsql_BindParamStr( sth, 1, new_dn.bv_val, BACKSQL_MAX_DN_LEN );
745         SQLBindParameter( sth, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
746                         0, 0, &e_id.oc_id, 0, 0 );
747         SQLBindParameter( sth, 3, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
748                         0, 0, &new_pid.id, 0, 0 );
749         SQLBindParameter( sth, 4, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
750                         0, 0, &e_id.keyval, 0, 0 );
751         rc = SQLExecDirect( sth, bi->insentry_query, SQL_NTS );
752         if ( rc != SQL_SUCCESS ) {
753                 Debug( LDAP_DEBUG_TRACE, "backsql_modrdn(): "
754                         "could not insert ldap_entries record\n", 0, 0, 0 );
755                 backsql_PrintErrors( bi->db_env, dbh, sth, rc );
756                 rs->sr_err = LDAP_OTHER;
757                 rs->sr_text = "SQL-backend error";
758                 send_ldap_result( op, rs );
759                 goto modrdn_return;
760         }
761
762         /*
763          * Get attribute type and attribute value of our new rdn,
764          * we will need to add that to our new entry
765          */
766         if ( ldap_bv2rdn( &op->oq_modrdn.rs_newrdn, &new_rdn,
767                                 (char **)&rs->sr_text, 
768                                 LDAP_DN_FORMAT_LDAP ) ) {
769 #ifdef NEW_LOGGING
770                 LDAP_LOG ( OPERATION, ERR, 
771                         "backsql_modrdn: can't figure out "
772                         "type(s)/values(s) of newrdn\n", 
773                         0, 0, 0 );
774 #else
775                 Debug( LDAP_DEBUG_TRACE,
776                         "backsql_modrdn: can't figure out "
777                         "type(s)/values(s) of newrdn\n", 
778                         0, 0, 0 );
779 #endif
780                 rs->sr_err = LDAP_INVALID_DN_SYNTAX;
781                 goto modrdn_return;
782         }
783
784 #ifdef NEW_LOGGING
785         LDAP_LOG ( OPERATION, RESULTS, 
786                 "backsql_modrdn: new_rdn_type=\"%s\", "
787                 "new_rdn_val=\"%s\"\n",
788                 new_rdn[ 0 ]->la_attr.bv_val, 
789                 new_rdn[ 0 ]->la_value.bv_val, 0 );
790 #else
791         Debug( LDAP_DEBUG_TRACE,
792                 "backsql_modrdn: new_rdn_type=\"%s\", "
793                 "new_rdn_val=\"%s\"\n",
794                 new_rdn[ 0 ]->la_attr.bv_val,
795                 new_rdn[ 0 ]->la_value.bv_val, 0 );
796 #endif
797
798         if ( op->oq_modrdn.rs_deleteoldrdn ) {
799                 if ( ldap_bv2rdn( &op->o_req_dn, &old_rdn,
800                                         (char **)&rs->sr_text,
801                                         LDAP_DN_FORMAT_LDAP ) ) {
802 #ifdef NEW_LOGGING
803                         LDAP_LOG ( OPERATION, ERR, 
804                                 "backsql_modrdn: can't figure out "
805                                 "type(s)/values(s) of old_rdn\n", 
806                                 0, 0, 0 );
807 #else
808                         Debug( LDAP_DEBUG_TRACE,
809                                 "backsql_modrdn: can't figure out "
810                                 "the old_rdn type(s)/value(s)\n", 
811                                 0, 0, 0 );
812 #endif
813                         rs->sr_err = LDAP_OTHER;
814                         goto modrdn_return;             
815                 }
816         }
817
818         e.e_name = new_dn;
819         e.e_nname = new_ndn;
820         rs->sr_err = slap_modrdn2mods( op, rs, &e, old_rdn, new_rdn, &mod );
821         if ( rs->sr_err != LDAP_SUCCESS ) {
822                 goto modrdn_return;
823         }
824
825         if ( !acl_check_modlist( op, &e, mod )) {
826                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
827                 goto modrdn_return;
828         }
829
830         oc = backsql_id2oc( bi, e_id.oc_id );
831         rs->sr_err = backsql_modify_internal( op, rs, dbh, oc, &e_id, mod );
832
833         if ( rs->sr_err == LDAP_SUCCESS ) {
834
835                 /*
836                  * Commit only if all operations succeed
837                  */
838                 SQLTransact( SQL_NULL_HENV, dbh,
839                                 op->o_noop ? SQL_ROLLBACK : SQL_COMMIT );
840         }
841
842 modrdn_return:
843         SQLFreeStmt( sth, SQL_DROP );
844
845         if ( new_dn.bv_val ) {
846                 ch_free( new_dn.bv_val );
847         }
848         
849         if ( new_ndn.bv_val ) {
850                 ch_free( new_ndn.bv_val );
851         }
852         
853         /* LDAP v2 supporting correct attribute handling. */
854         if ( new_rdn != NULL ) {
855                 ldap_rdnfree( new_rdn );
856         }
857         if ( old_rdn != NULL ) {
858                 ldap_rdnfree( old_rdn );
859         }
860         if( mod != NULL ) {
861                 Modifications *tmp;
862                 for (; mod; mod=tmp ) {
863                         tmp = mod->sml_next;
864                         free( mod );
865                 }
866         }
867
868         send_ldap_result( op, rs );
869
870         Debug( LDAP_DEBUG_TRACE, "<==backsql_modrdn()\n", 0, 0, 0 );
871         return op->o_noop;
872 }
873
874 int
875 backsql_add( Operation *op, SlapReply *rs )
876 {
877         backsql_info            *bi = (backsql_info*)op->o_bd->be_private;
878         SQLHDBC                 dbh;
879         SQLHSTMT                sth;
880         unsigned long           new_keyval = 0;
881         long                    i;
882         RETCODE                 rc;
883         backsql_oc_map_rec      *oc = NULL;
884         backsql_at_map_rec      *at_rec = NULL;
885         backsql_entryID         e_id, parent_id;
886         Entry                   p;
887         Attribute               *at;
888         struct berval           *at_val;
889         struct berval           pdn;
890         /* first parameter #, parameter order */
891         SQLUSMALLINT            pno, po;
892         /* procedure return code */
893         int                     prc;
894
895         Debug( LDAP_DEBUG_TRACE, "==>backsql_add(): adding entry '%s'\n",
896                         op->oq_add.rs_e->e_name.bv_val, 0, 0 );
897
898         /* check schema */
899         if ( global_schemacheck ) {
900                 char            textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };
901
902                 rs->sr_err = entry_schema_check( op->o_bd, op->oq_add.rs_e,
903                                 NULL,
904                                 &rs->sr_text, textbuf, sizeof( textbuf ) );
905                 if ( rs->sr_err != LDAP_SUCCESS ) {
906                         Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
907                                 "entry failed schema check -- aborting\n",
908                                 0, 0, 0 );
909                         send_ldap_result( op, rs );
910                         return 1;
911                 }
912         }
913
914         /* search structural objectClass */
915         for ( at = op->oq_add.rs_e->e_attrs; at != NULL; at = at->a_next ) {
916                 if ( at->a_desc == slap_schema.si_ad_structuralObjectClass ) {
917                         break;
918                 }
919         }
920
921         /* there must exist */
922         assert( at != NULL );
923
924         /* I guess we should play with sub/supertypes to find a suitable oc */
925         oc = backsql_name2oc( bi, &at->a_vals[0] );
926
927         if ( oc == NULL ) {
928                 Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
929                         "cannot determine objectclass of entry -- aborting\n",
930                         0, 0, 0 );
931                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
932                 rs->sr_text = "operation not permitted within namingContext";
933                 send_ldap_result( op, rs );
934                 return 1;
935         }
936
937         if ( oc->create_proc == NULL ) {
938                 Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
939                         "create procedure is not defined for this objectclass "
940                         "- aborting\n", 0, 0, 0 );
941                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
942                 rs->sr_text = "operation not permitted within namingContext";
943                 send_ldap_result( op, rs );
944                 return 1;
945
946         } else if ( BACKSQL_CREATE_NEEDS_SELECT( bi )
947                         && oc->create_keyval == NULL ) {
948                 Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
949                         "create procedure needs select procedure, "
950                         "but none is defined - aborting\n", 0, 0, 0 );
951                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
952                 rs->sr_text = "operation not permitted within namingContext";
953                 send_ldap_result( op, rs );
954                 return 1;
955         }
956
957         rs->sr_err = backsql_get_db_conn( op, &dbh );
958         if ( rs->sr_err != LDAP_SUCCESS ) {
959                 Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
960                         "could not get connection handle - exiting\n", 
961                         0, 0, 0 );
962                 rs->sr_text = ( rs->sr_err == LDAP_OTHER )
963                         ?  "SQL-backend error" : NULL,
964                 send_ldap_result( op, rs );
965                 return 1;
966         }
967
968         /*
969          * Check if entry exists
970          */
971         rs->sr_err = backsql_dn2id( bi, &e_id, dbh, &op->oq_add.rs_e->e_name );
972         if ( rs->sr_err == LDAP_SUCCESS ) {
973                 Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
974                         "entry '%s' exists\n",
975                         op->oq_add.rs_e->e_name.bv_val, 0, 0 );
976                 rs->sr_err = LDAP_ALREADY_EXISTS;
977                 send_ldap_result( op, rs );
978                 return 1;
979         }
980
981         /*
982          * Check if parent exists
983          */
984         dnParent( &op->oq_add.rs_e->e_name, &pdn );
985         rs->sr_err = backsql_dn2id( bi, &parent_id, dbh, &pdn );
986         if ( rs->sr_err != LDAP_SUCCESS ) {
987                 Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
988                         "could not lookup parent entry for new record '%s'\n",
989                         pdn.bv_val, 0, 0 );
990
991                 if ( rs->sr_err != LDAP_NO_SUCH_OBJECT ) {
992                         send_ldap_result( op, rs );
993                         return 1;
994                 }
995
996                 /*
997                  * Look for matched
998                  */
999                 while ( 1 ) {
1000                         struct berval   dn;
1001                         char            *matched = NULL;
1002
1003                         dn = pdn;
1004                         dnParent( &dn, &pdn );
1005
1006                         /*
1007                          * Empty DN ("") defaults to LDAP_SUCCESS
1008                          */
1009                         rs->sr_err = backsql_dn2id( bi, &parent_id, dbh, &pdn );
1010                         switch ( rs->sr_err ) {
1011                         case LDAP_NO_SUCH_OBJECT:
1012                                 if ( pdn.bv_len > 0 ) {
1013                                         break;
1014                                 }
1015                                 /* fail over to next case */
1016                                 
1017                         case LDAP_SUCCESS:
1018                                 matched = pdn.bv_val;
1019                                 /* fail over to next case */
1020
1021                         default:
1022                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
1023                                 rs->sr_matched = matched;
1024                                 send_ldap_result( op, rs );
1025                                 return 1;
1026                         } 
1027                 }
1028         }
1029
1030         /*
1031          * create_proc is executed; if expect_return is set, then
1032          * an output parameter is bound, which should contain 
1033          * the id of the added row; otherwise the procedure
1034          * is expected to return the id as the first column of a select
1035          */
1036
1037         p.e_attrs = NULL;
1038         p.e_name = pdn;
1039         dnParent( &op->oq_add.rs_e->e_nname, &p.e_nname );
1040         if ( !access_allowed( op, &p, slap_schema.si_ad_children,
1041                                 NULL, ACL_WRITE, NULL ) ) {
1042                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1043                 send_ldap_result( op, rs );
1044                 return 1;
1045         }
1046
1047         rc = SQLAllocStmt( dbh, &sth );
1048         if ( rc != SQL_SUCCESS ) {
1049                 rs->sr_err = LDAP_OTHER;
1050                 rs->sr_text = "SQL-backend error";
1051                 send_ldap_result( op, rs );
1052                 return 1;
1053         }
1054
1055         if ( BACKSQL_IS_ADD( oc->expect_return ) ) {
1056                 SQLBindParameter( sth, 1, SQL_PARAM_OUTPUT, SQL_C_ULONG, 
1057                                 SQL_INTEGER, 0, 0, &new_keyval, 0, 0 );
1058         }
1059
1060         Debug( LDAP_DEBUG_TRACE, "backsql_add(): executing '%s'\n",
1061                 oc->create_proc, 0, 0 );
1062         rc = SQLExecDirect( sth, oc->create_proc, SQL_NTS );
1063         if ( rc != SQL_SUCCESS ) {
1064                 Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1065                         "create_proc execution failed\n", 0, 0, 0 );
1066                 backsql_PrintErrors( bi->db_env, dbh, sth, rc);
1067                 SQLFreeStmt( sth, SQL_DROP );
1068                 rs->sr_err = LDAP_OTHER;
1069                 rs->sr_text = "SQL-backend error";
1070                 send_ldap_result( op, rs );
1071                 return 1;
1072         }
1073         if ( op->o_noop ) {
1074                 SQLTransact( SQL_NULL_HENV, dbh, SQL_ROLLBACK );
1075         }
1076
1077         if ( !BACKSQL_IS_ADD( oc->expect_return ) ) {
1078                 SWORD           ncols;
1079                 SQLINTEGER      value_len;
1080
1081                 if ( BACKSQL_CREATE_NEEDS_SELECT( bi ) ) {
1082 #ifndef BACKSQL_REALLOC_STMT
1083                         SQLFreeStmt( sth, SQL_RESET_PARAMS );
1084 #else /* BACKSQL_REALLOC_STMT */
1085                         SQLFreeStmt( sth, SQL_DROP );
1086                         rc = SQLAllocStmt( dbh, &sth );
1087                         if ( rc != SQL_SUCCESS ) {
1088                                 rs->sr_err = LDAP_OTHER;
1089                                 rs->sr_text = "SQL-backend error";
1090                                 send_ldap_result( op, rs );
1091                                 return 1;
1092                         }
1093 #endif /* BACKSQL_REALLOC_STMT */
1094
1095                         rc = SQLExecDirect( sth, oc->create_keyval, SQL_NTS );
1096                         if ( rc != SQL_SUCCESS ) {
1097                                 rs->sr_err = LDAP_OTHER;
1098                                 rs->sr_text = "SQL-backend error";
1099                                 send_ldap_result( op, rs );
1100                                 return 1;
1101                         }
1102                 }
1103
1104                 /*
1105                  * the query to know the id of the inserted entry
1106                  * must be embedded in the create procedure
1107                  */
1108                 rc = SQLNumResultCols( sth, &ncols );
1109                 if ( rc != SQL_SUCCESS ) {
1110                         Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1111                                 "create_proc result evaluation failed\n",
1112                                 0, 0, 0 );
1113                         backsql_PrintErrors( bi->db_env, dbh, sth, rc);
1114                         SQLFreeStmt( sth, SQL_DROP );
1115                         rs->sr_err = LDAP_OTHER;
1116                         rs->sr_text = "SQL-backend error";
1117                         send_ldap_result( op, rs );
1118                         return 1;
1119
1120                 } else if ( ncols != 1 ) {
1121                         Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1122                                 "create_proc result is bogus (ncols=%d)\n",
1123                                 ncols, 0, 0 );
1124                         backsql_PrintErrors( bi->db_env, dbh, sth, rc);
1125                         SQLFreeStmt( sth, SQL_DROP );
1126                         rs->sr_err = LDAP_OTHER;
1127                         rs->sr_text = "SQL-backend error";
1128                         send_ldap_result( op, rs );
1129                         return 1;
1130                 }
1131
1132 #if 0
1133                 {
1134                         SQLCHAR         colname[ 64 ];
1135                         SQLSMALLINT     name_len, col_type, col_scale, col_null;
1136                         UDWORD          col_prec;
1137
1138                         /*
1139                          * FIXME: check whether col_type is compatible,
1140                          * if it can be null and so on ...
1141                          */
1142                         rc = SQLDescribeCol( sth, (SQLUSMALLINT)1, 
1143                                         &colname[ 0 ], 
1144                                         (SQLUINTEGER)( sizeof( colname ) - 1 ),
1145                                         &name_len, &col_type,
1146                                         &col_prec, &col_scale, &col_null );
1147                 }
1148 #endif
1149
1150                 rc = SQLBindCol( sth, (SQLUSMALLINT)1, SQL_C_ULONG,
1151                                 (SQLPOINTER)&new_keyval, 
1152                                 (SQLINTEGER)sizeof( new_keyval ), 
1153                                 &value_len );
1154
1155                 rc = SQLFetch( sth );
1156
1157                 if ( value_len <= 0 ) {
1158                         Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1159                                 "create_proc result is empty?\n",
1160                                 0, 0, 0 );
1161                         backsql_PrintErrors( bi->db_env, dbh, sth, rc);
1162                         SQLFreeStmt( sth, SQL_DROP );
1163                         rs->sr_err = LDAP_OTHER;
1164                         rs->sr_text = "SQL-backend error";
1165                         send_ldap_result( op, rs );
1166                         return 1;
1167                 }
1168         }
1169
1170 #ifndef BACKSQL_REALLOC_STMT
1171         SQLFreeStmt( sth, SQL_RESET_PARAMS );
1172 #else /* BACKSQL_REALLOC_STMT */
1173         SQLFreeStmt( sth, SQL_DROP );
1174 #endif /* BACKSQL_REALLOC_STMT */
1175
1176         Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1177                 "create_proc returned keyval=%ld\n", new_keyval, 0, 0 );
1178
1179         for ( at = op->oq_add.rs_e->e_attrs; at != NULL; at = at->a_next ) {
1180                 SQLUSMALLINT    currpos;
1181
1182                 Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1183                         "adding attribute '%s'\n", 
1184                         at->a_desc->ad_cname.bv_val, 0, 0 );
1185
1186                 /*
1187                  * Skip:
1188                  * - the first occurrence of objectClass, which is used
1189                  *   to determine how to bulid the SQL entry (FIXME ?!?)
1190                  * - operational attributes
1191                  *   empty attributes (FIXME ?!?)
1192                  */
1193                 if ( backsql_attr_skip( at->a_desc, at->a_vals ) ) {
1194                         continue;
1195                 }
1196
1197                 at_rec = backsql_ad2at( oc, at->a_desc ); 
1198   
1199                 if ( at_rec == NULL ) {
1200                         Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1201                                 "attribute '%s' is not registered "
1202                                 "in objectclass '%s'\n",
1203                                 at->a_desc->ad_cname.bv_val,
1204                                 BACKSQL_OC_NAME( oc ), 0 );
1205
1206                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
1207                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1208                                 rs->sr_text = "operation not permitted "
1209                                         "within namingContext";
1210                                 send_ldap_result( op, rs );
1211                                 return 1;
1212                         }
1213
1214                         continue;
1215                 }
1216                 
1217                 if ( at_rec->add_proc == NULL ) {
1218                         Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1219                                 "add procedure is not defined "
1220                                 "for attribute '%s'\n",
1221                                 at->a_desc->ad_cname.bv_val, 0, 0 );
1222
1223                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
1224                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1225                                 rs->sr_text = "operation not permitted "
1226                                         "within namingContext";
1227                                 send_ldap_result( op, rs );
1228                                 return 1;
1229                         }
1230
1231                         continue;
1232                 }
1233
1234 #ifdef BACKSQL_REALLOC_STMT
1235                 rc = backsql_Prepare( dbh, &sth, at_rec->add_proc, 0 );
1236                 if ( rc != SQL_SUCCESS ) {
1237
1238                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
1239                                 rs->sr_err = LDAP_OTHER;
1240                                 rs->sr_text = "SQL-backend error";
1241                                 send_ldap_result( op, rs );
1242                                 return 1;
1243                         }
1244
1245                         continue;
1246                 }
1247 #endif /* BACKSQL_REALLOC_STMT */
1248
1249                 if ( BACKSQL_IS_ADD( at_rec->expect_return ) ) {
1250                         pno = 1;
1251                         SQLBindParameter( sth, 1, SQL_PARAM_OUTPUT,
1252                                         SQL_C_ULONG, SQL_INTEGER,
1253                                         0, 0, &prc, 0, 0 );
1254                 } else {
1255                         pno = 0;
1256                 }
1257
1258                 po = ( BACKSQL_IS_ADD( at_rec->param_order ) ) > 0;
1259                 currpos = pno + 1 + po;
1260                 SQLBindParameter( sth, currpos,
1261                                 SQL_PARAM_INPUT, SQL_C_ULONG,
1262                                 SQL_INTEGER, 0, 0, &new_keyval, 0, 0 );
1263                 currpos = pno + 2 - po;
1264
1265                 for ( i = 0, at_val = &at->a_vals[ i ];
1266                                 at_val->bv_val != NULL;
1267                                 i++, at_val = &at->a_vals[ i ] ) {
1268
1269                         /*
1270                          * Do not deal with the objectClass that is used
1271                          * to build the entry
1272                          */
1273                         if ( at->a_desc == slap_schema.si_ad_objectClass ) {
1274                                 if ( bvmatch( at_val, &oc->oc->soc_cname ) ) {
1275                                         continue;
1276                                 }
1277                         }
1278
1279                         /*
1280                          * check for syntax needed here 
1281                          * maybe need binary bind?
1282                          */
1283
1284                         backsql_BindParamStr( sth, currpos,
1285                                         at_val->bv_val, at_val->bv_len + 1 );
1286 #ifdef SECURITY_PARANOID
1287                         Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1288                                 "executing '%s', id=%ld\n", 
1289                                 at_rec->add_proc, new_keyval, 0 );
1290 #else
1291                         Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1292                                 "executing '%s' with val='%s', id=%ld\n", 
1293                                 at_rec->add_proc, at_val->bv_val, new_keyval );
1294 #endif
1295 #ifndef BACKSQL_REALLOC_STMT
1296                         rc = SQLExecDirect( sth, at_rec->add_proc, SQL_NTS );
1297 #else /* BACKSQL_REALLOC_STMT */
1298                         rc = SQLExecute( sth );
1299 #endif /* BACKSQL_REALLOC_STMT */
1300                         if ( rc != SQL_SUCCESS ) {
1301                                 Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1302                                         "add_proc execution failed\n", 
1303                                         0, 0, 0 );
1304                                 backsql_PrintErrors( bi->db_env, dbh, sth, rc );
1305
1306                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
1307                                         rs->sr_err = LDAP_OTHER;
1308                                         rs->sr_text = "SQL-backend error";
1309                                         send_ldap_result( op, rs );
1310                                         return 1;
1311                                 }
1312                         }
1313                 }
1314 #ifndef BACKSQL_REALLOC_STMT
1315                 SQLFreeStmt( sth, SQL_RESET_PARAMS ); 
1316 #else /* BACKSQL_REALLOC_STMT */
1317                 SQLFreeStmt( sth, SQL_DROP );
1318 #endif /* BACKSQL_REALLOC_STMT */
1319         }
1320
1321 #ifdef BACKSQL_REALLOC_STMT
1322         rc = backsql_Prepare( dbh, &sth, bi->insentry_query, 0 );
1323         if ( rc != SQL_SUCCESS ) {
1324                 rs->sr_err = LDAP_OTHER;
1325                 rs->sr_text = "SQL-backend error";
1326                 send_ldap_result( op, rs );
1327                 return 1;
1328         }
1329 #endif /* BACKSQL_REALLOC_STMT */
1330         
1331         backsql_BindParamStr( sth, 1, op->oq_add.rs_e->e_name.bv_val,
1332                         BACKSQL_MAX_DN_LEN );
1333         SQLBindParameter( sth, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
1334                         0, 0, &oc->id, 0, 0 );
1335         SQLBindParameter( sth, 3, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
1336                         0, 0, &parent_id.id, 0, 0 );
1337         SQLBindParameter( sth, 4, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
1338                         0, 0, &new_keyval, 0, 0 );
1339
1340         Debug( LDAP_DEBUG_TRACE, "backsql_add(): executing '%s' for dn '%s'\n",
1341                         bi->insentry_query, op->oq_add.rs_e->e_name.bv_val, 0 );
1342         Debug( LDAP_DEBUG_TRACE, " for oc_map_id=%ld, parent_id=%ld, "
1343                         "keyval=%ld\n", oc->id, parent_id.id, new_keyval );
1344 #ifndef BACKSQL_REALLOC_STMT
1345         rc = SQLExecDirect( sth, bi->insentry_query, SQL_NTS );
1346 #else /* BACKSQL_REALLOC_STMT */
1347         rc = SQLExecute( sth );
1348 #endif /* BACKSQL_REALLOC_STMT */
1349         if ( rc != SQL_SUCCESS ) {
1350                 Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
1351                         "could not insert ldap_entries record\n", 0, 0, 0 );
1352                 backsql_PrintErrors( bi->db_env, dbh, sth, rc );
1353                 
1354                 /*
1355                  * execute delete_proc to delete data added !!!
1356                  */
1357                 SQLFreeStmt( sth, SQL_DROP );
1358                 rs->sr_err = LDAP_OTHER;
1359                 rs->sr_text = "SQL-backend error";
1360                 send_ldap_result( op, rs );
1361                 return 1;
1362         }
1363         
1364         SQLFreeStmt( sth, SQL_DROP );
1365
1366         /*
1367          * Commit only if all operations succeed
1368          */
1369         SQLTransact( SQL_NULL_HENV, dbh, 
1370                         op->o_noop ? SQL_ROLLBACK : SQL_COMMIT );
1371
1372         /*
1373          * FIXME: NOOP does not work for add -- it works for all 
1374          * the other operations, and I don't get the reason :(
1375          */
1376
1377         send_ldap_result( op, rs );
1378
1379         return op->o_noop;
1380 }
1381
1382 int
1383 backsql_delete( Operation *op, SlapReply *rs )
1384 {
1385         backsql_info            *bi = (backsql_info*)op->o_bd->be_private;
1386         SQLHDBC                 dbh;
1387         SQLHSTMT                sth;
1388         RETCODE                 rc;
1389         backsql_oc_map_rec      *oc = NULL;
1390         backsql_entryID         e_id;
1391         Entry                   e;
1392         /* first parameter no */
1393         SQLUSMALLINT            pno;
1394
1395         Debug( LDAP_DEBUG_TRACE, "==>backsql_delete(): deleting entry '%s'\n",
1396                         op->o_req_ndn.bv_val, 0, 0 );
1397
1398         dnParent( &op->o_req_dn, &e.e_name );
1399         dnParent( &op->o_req_ndn, &e.e_nname );
1400         e.e_attrs = NULL;
1401
1402         /* check parent for "children" acl */
1403         if ( !access_allowed( op, &e, slap_schema.si_ad_children, 
1404                         NULL, ACL_WRITE, NULL ) ) {
1405                 Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
1406                         "no write access to parent\n", 
1407                         0, 0, 0 );
1408                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
1409                 send_ldap_result( op, rs );
1410                 return 1;
1411
1412         }
1413         
1414         rs->sr_err = backsql_get_db_conn( op, &dbh );
1415         if ( rs->sr_err != LDAP_SUCCESS ) {
1416                 Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
1417                         "could not get connection handle - exiting\n", 
1418                         0, 0, 0 );
1419                 rs->sr_text = ( rs->sr_err == LDAP_OTHER )
1420                         ? "SQL-backend error" : NULL,
1421                 send_ldap_result( op, rs );
1422                 return 1;
1423         }
1424         
1425         rs->sr_err = backsql_dn2id( bi, &e_id, dbh, &op->o_req_ndn );
1426         if ( rs->sr_err != LDAP_SUCCESS ) {
1427                 Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
1428                         "could not lookup entry id\n", 0, 0, 0 );
1429                 send_ldap_result( op, rs );
1430                 return 1;
1431         }
1432
1433         rs->sr_err = backsql_has_children( bi, dbh, &op->o_req_ndn );
1434         switch ( rs->sr_err ) {
1435         case LDAP_COMPARE_TRUE:
1436                 Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
1437                         "entry \"%s\" has children\n",
1438                         op->o_req_dn.bv_val, 0, 0 );
1439                 rs->sr_err = LDAP_NOT_ALLOWED_ON_NONLEAF;
1440                 rs->sr_text = "subtree delete not supported";
1441                 send_ldap_result( op, rs );
1442                 return 1;
1443
1444         case LDAP_COMPARE_FALSE:
1445                 break;
1446
1447         default:
1448                 send_ldap_result( op, rs );
1449                 return 1;
1450         }
1451
1452         oc = backsql_id2oc( bi, e_id.oc_id );
1453         if ( oc == NULL ) {
1454                 Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
1455                         "cannot determine objectclass of entry -- aborting\n",
1456                         0, 0, 0 );
1457                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1458                 rs->sr_text = "operation not permitted within namingContext";
1459                 send_ldap_result( op, rs );
1460                 return 1;
1461         }
1462
1463         if ( oc->delete_proc == NULL ) {
1464                 Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
1465                         "delete procedure is not defined "
1466                         "for this objectclass - aborting\n", 0, 0, 0 );
1467                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
1468                 rs->sr_text = "operation not permitted within namingContext";
1469                 send_ldap_result( op, rs );
1470                 return 1;
1471         }
1472
1473         SQLAllocStmt( dbh, &sth );
1474         if ( BACKSQL_IS_DEL( oc->expect_return ) ) {
1475                 pno = 1;
1476                 SQLBindParameter( sth, 1, SQL_PARAM_OUTPUT, SQL_C_ULONG,
1477                                 SQL_INTEGER, 0, 0, &rc, 0, 0 );
1478         } else {
1479                 pno = 0;
1480         }
1481
1482         SQLBindParameter( sth, pno + 1, SQL_PARAM_INPUT, 
1483                         SQL_C_ULONG, SQL_INTEGER, 0, 0, &e_id.keyval, 0, 0 );
1484
1485         Debug( LDAP_DEBUG_TRACE, "backsql_delete(): executing '%s'\n",
1486                         oc->delete_proc, 0, 0 );
1487         rc = SQLExecDirect( sth, oc->delete_proc, SQL_NTS );
1488         if ( rc != SQL_SUCCESS ) {
1489                 Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
1490                         "delete_proc execution failed\n", 0, 0, 0 );
1491                 backsql_PrintErrors( bi->db_env, dbh, sth, rc );
1492                 SQLFreeStmt( sth, SQL_DROP );
1493                 rs->sr_err = LDAP_OTHER;
1494                 rs->sr_text = "SQL-backend error";
1495                 send_ldap_result( op, rs );
1496                 return 1;
1497         }
1498 #ifndef BACKSQL_REALLOC_STMT
1499         SQLFreeStmt( sth, SQL_RESET_PARAMS );
1500 #else /* BACKSQL_REALLOC_STMT */
1501         SQLFreeStmt( sth, SQL_DROP );
1502         SQLAllocStmt( dbh, &sth );
1503 #endif /* BACKSQL_REALLOC_STMT */
1504
1505         SQLBindParameter( sth, 1, SQL_PARAM_INPUT, SQL_C_ULONG, SQL_INTEGER,
1506                         0, 0, &e_id.id, 0, 0 );
1507         rc = SQLExecDirect( sth, bi->delentry_query, SQL_NTS );
1508         if ( rc != SQL_SUCCESS ) {
1509                 Debug( LDAP_DEBUG_TRACE, "backsql_delete(): "
1510                         "failed to delete record from ldap_entries\n", 
1511                         0, 0, 0 );
1512                 backsql_PrintErrors( bi->db_env, dbh, sth, rc );
1513                 SQLFreeStmt( sth, SQL_DROP );
1514                 rs->sr_err = LDAP_OTHER;
1515                 rs->sr_text = "SQL-backend error";
1516                 send_ldap_result( op, rs );
1517                 return 1;
1518         }
1519         
1520         SQLFreeStmt( sth, SQL_DROP );
1521
1522         /*
1523          * Commit only if all operations succeed
1524          *
1525          * FIXME: backsql_add() does not fail if add operations 
1526          * are not available for some attributes, or if
1527          * a multiple value add actually results in a replace, 
1528          * or if a single operation on an attribute fails 
1529          * for any reason
1530          */
1531         SQLTransact( SQL_NULL_HENV, dbh, 
1532                         op->o_noop ? SQL_ROLLBACK : SQL_COMMIT );
1533
1534         rs->sr_err = LDAP_SUCCESS;
1535         send_ldap_result( op, rs );
1536         Debug( LDAP_DEBUG_TRACE, "<==backsql_delete()\n", 0, 0, 0 );
1537
1538         return op->o_noop;
1539 }
1540
1541 #endif /* SLAPD_SQL */
1542