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