]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/add.c
Added provisions for a layer between the backend and the ODBC
[openldap] / servers / slapd / back-sql / add.c
1 /* $OpenLDAP$ */
2 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
3  *
4  * Copyright 1999-2004 The OpenLDAP Foundation.
5  * Portions Copyright 1999 Dmitry Kovalev.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted only as authorized by the OpenLDAP
10  * Public License.
11  *
12  * A copy of this license is available in the file LICENSE in the
13  * top-level directory of the distribution or, alternatively, at
14  * <http://www.OpenLDAP.org/license.html>.
15  */
16 /* ACKNOWLEDGEMENTS:
17  * This work was initially developed by Dmitry Kovalev for inclusion
18  * by OpenLDAP Software.
19  */
20
21 #include "portable.h"
22
23 #ifdef SLAPD_SQL
24
25 #include <stdio.h>
26 #include <sys/types.h>
27 #include "ac/string.h"
28
29 #include "slap.h"
30 #include "ldap_pvt.h"
31 #include "proto-sql.h"
32
33 /*
34  * Skip:
35  * - the first occurrence of objectClass, which is used
36  *   to determine how to build the SQL entry (FIXME ?!?)
37  * - operational attributes
38  *   empty attributes (FIXME ?!?)
39  */
40 #define backsql_attr_skip(ad,vals) \
41         ( \
42                 ( (ad) == slap_schema.si_ad_objectClass \
43                                 && (vals)[ 1 ].bv_val == NULL ) \
44                 || is_at_operational( (ad)->ad_type ) \
45                 || ( (vals)[ 0 ].bv_val == NULL ) \
46         )
47
48 int
49 backsql_modify_internal(
50         Operation               *op,
51         SlapReply               *rs,
52         SQLHDBC                 dbh, 
53         backsql_oc_map_rec      *oc,
54         backsql_entryID         *e_id,
55         Modifications           *modlist )
56 {
57         backsql_info    *bi = (backsql_info*)op->o_bd->be_private;
58         RETCODE         rc;
59         SQLHSTMT        sth;
60         Modifications   *ml;
61
62         Debug( LDAP_DEBUG_TRACE, "==>backsql_modify_internal(): "
63                 "traversing modifications list\n", 0, 0, 0 );
64
65 #ifndef BACKSQL_REALLOC_STMT
66         SQLAllocStmt( dbh, &sth );
67 #endif /* BACKSQL_REALLOC_STMT */
68
69         for ( ml = modlist; ml != NULL; ml = ml->sml_next ) {
70                 AttributeDescription    *ad;
71                 backsql_at_map_rec      *at = NULL;
72                 struct berval           *at_val;
73                 Modification            *c_mod;
74                 int                     i;
75                 /* first parameter no, parameter order */
76                 SQLUSMALLINT            pno, po;
77                 /* procedure return code */
78                 int                     prc;
79
80 #ifdef BACKSQL_REALLOC_STMT
81                 SQLAllocStmt( dbh, &sth );
82 #endif /* BACKSQL_REALLOC_STMT */
83
84                 c_mod = &ml->sml_mod;
85                 ad = c_mod->sm_desc;
86
87                 Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
88                         "modifying attribute \"%s\" according to "
89                         "mappings for objectClass \"%s\"\n",
90                         ad->ad_cname.bv_val, BACKSQL_OC_NAME( oc ), 0 );
91
92                 if ( backsql_attr_skip( ad, c_mod->sm_values ) ) {
93                         continue;
94                 }
95
96                 at = backsql_ad2at( oc, ad );
97                 if ( at == NULL ) {
98                         Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
99                                 "attribute \"%s\" is not registered "
100                                 "in objectClass \"%s\"\n",
101                                 ad->ad_cname.bv_val, oc, 0 );
102
103                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
104                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
105                                 rs->sr_text = "operation not permitted "
106                                         "within namingContext";
107                                 goto done;
108                         }
109
110                         continue;
111                 }
112   
113                 switch( c_mod->sm_op ) {
114                 case LDAP_MOD_REPLACE: {
115                         SQLHSTMT asth;
116                         BACKSQL_ROW_NTS row;
117                         
118                         Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
119                                 "replacing values for attribute \"%s\"\n",
120                                 at->bam_ad->ad_cname.bv_val, 0, 0 );
121
122                         if ( at->bam_add_proc == NULL ) {
123                                 Debug( LDAP_DEBUG_TRACE,
124                                         "   backsql_modify_internal(): "
125                                         "add procedure is not defined "
126                                         "for attribute \"%s\" "
127                                         "- unable to perform replacements\n",
128                                         at->bam_ad->ad_cname.bv_val, 0, 0 );
129
130                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
131                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
132                                         rs->sr_text = "operation not permitted "
133                                                 "within namingContext";
134                                         goto done;
135                                 }
136
137                                 break;
138                         }
139
140                         if ( at->bam_delete_proc == NULL ) {
141                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
142                                         Debug( LDAP_DEBUG_TRACE,
143                                                 "   backsql_modify_internal(): "
144                                                 "delete procedure is not defined "
145                                                 "for attribute \"%s\"\n",
146                                                 at->bam_ad->ad_cname.bv_val, 0, 0 );
147
148                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
149                                         rs->sr_text = "operation not permitted "
150                                                 "within namingContext";
151                                         goto done;
152                                 }
153
154                                 Debug( LDAP_DEBUG_TRACE,
155                                         "   backsql_modify_internal(): "
156                                         "delete procedure is not defined "
157                                         "for attribute \"%s\" "
158                                         "- adding only\n",
159                                         at->bam_ad->ad_cname.bv_val, 0, 0 );
160
161                                 goto add_only;
162                         }
163                         
164 del_all:
165                         rc = backsql_Prepare( dbh, &asth, at->bam_query, 0 );
166                         if ( rc != SQL_SUCCESS ) {
167                                 Debug( LDAP_DEBUG_TRACE,
168                                         "   backsql_modify_internal(): "
169                                         "error preparing query\n", 0, 0, 0 );
170                                 backsql_PrintErrors( bi->db_env, dbh, 
171                                                 asth, rc );
172
173                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
174                                         rs->sr_err = LDAP_OTHER;
175                                         rs->sr_text = "SQL-backend error";
176                                         goto done;
177                                 }
178
179                                 break;
180                         }
181
182 #ifdef BACKSQL_ARBITRARY_KEY
183                         rc = backsql_BindParamStr( asth, 1,
184                                         e_id->eid_keyval.bv_val,
185                                         BACKSQL_MAX_KEY_LEN );
186 #else /* ! BACKSQL_ARBITRARY_KEY */
187                         rc = backsql_BindParamID( asth, 1, &e_id->eid_keyval );
188 #endif /* ! BACKSQL_ARBITRARY_KEY */
189                         if ( rc != SQL_SUCCESS ) {
190                                 Debug( LDAP_DEBUG_TRACE,
191                                         "   backsql_modify_internal(): "
192                                         "error binding key value parameter\n",
193                                         0, 0, 0 );
194                                 backsql_PrintErrors( bi->db_env, dbh, 
195                                                 asth, rc );
196                                 SQLFreeStmt( asth, SQL_DROP );
197
198                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
199                                         rs->sr_err = LDAP_OTHER;
200                                         rs->sr_text = "SQL-backend error";
201                                         goto done;
202                                 }
203
204                                 break;
205                         }
206                         
207                         rc = SQLExecute( asth );
208                         if ( !BACKSQL_SUCCESS( rc ) ) {
209                                 Debug( LDAP_DEBUG_TRACE,
210                                         "   backsql_modify_internal(): "
211                                         "error executing attribute query\n",
212                                         0, 0, 0 );
213                                 backsql_PrintErrors( bi->db_env, dbh, 
214                                                 asth, rc );
215                                 SQLFreeStmt( asth, SQL_DROP );
216
217                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
218                                         rs->sr_err = LDAP_OTHER;
219                                         rs->sr_text = "SQL-backend error";
220                                         goto done;
221                                 }
222
223                                 break;
224                         }
225
226                         backsql_BindRowAsStrings( asth, &row );
227                         rc = SQLFetch( asth );
228                         for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( asth ) ) {
229                                 for ( i = 0; i < row.ncols; i++ ) {
230                                         if ( BACKSQL_IS_DEL( at->bam_expect_return ) ) {
231                                                 pno = 1;
232                                                 SQLBindParameter(sth, 1,
233                                                         SQL_PARAM_OUTPUT,
234                                                         SQL_C_ULONG,
235                                                         SQL_INTEGER,
236                                                         0, 0, &prc, 0, 0 );
237                                         } else {
238                                                 pno = 0;
239                                         }
240                                         po = ( BACKSQL_IS_DEL( at->bam_param_order ) ) > 0;
241 #ifdef BACKSQL_ARBITRARY_KEY
242                                         SQLBindParameter( sth, pno + 1 + po,
243                                                 SQL_PARAM_INPUT,
244                                                 SQL_C_CHAR, SQL_VARCHAR,
245                                                 0, 0, e_id->eid_keyval.bv_val, 
246                                                 0, 0 );
247 #else /* ! BACKSQL_ARBITRARY_KEY */
248                                         SQLBindParameter( sth, pno + 1 + po,
249                                                 SQL_PARAM_INPUT,
250                                                 SQL_C_ULONG, SQL_INTEGER,
251                                                 0, 0, &e_id->eid_keyval, 0, 0 );
252 #endif /* ! BACKSQL_ARBITRARY_KEY */
253
254                                         /*
255                                          * check for syntax needed here 
256                                          * maybe need binary bind?
257                                          */
258                                         SQLBindParameter(sth, pno + 2 - po,
259                                                 SQL_PARAM_INPUT,
260                                                 SQL_C_CHAR, SQL_CHAR,
261                                                 0, 0, row.cols[ i ],
262                                                 strlen( row.cols[ i ] ), 0 );
263                          
264                                         Debug( LDAP_DEBUG_TRACE, 
265                                                 "   backsql_modify_internal(): "
266                                                 "executing \"%s\"\n",
267                                                 at->bam_delete_proc, 0, 0 );
268                                         rc = SQLExecDirect( sth,
269                                                 at->bam_delete_proc, SQL_NTS );
270                                         if ( rc != SQL_SUCCESS ) {
271                                                 Debug( LDAP_DEBUG_TRACE,
272                                                         "   backsql_modify_internal(): "
273                                                         "delete_proc "
274                                                         "execution failed\n",
275                                                         0, 0, 0 );
276                                                 backsql_PrintErrors( bi->db_env,
277                                                                 dbh, sth, rc );
278
279                                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
280                                                         rs->sr_err = LDAP_OTHER;
281                                                         rs->sr_text = "SQL-backend error";
282                                                         goto done;
283                                                 }
284                                         }
285 #ifdef BACKSQL_REALLOC_STMT
286                                         SQLFreeStmt( sth, SQL_DROP );
287                                         SQLAllocStmt( dbh, &sth );
288 #endif /* BACKSQL_REALLOC_STMT */
289                                 }
290                         }
291                         backsql_FreeRow( &row );
292                         SQLFreeStmt( asth, SQL_DROP );
293                 }
294
295                 /*
296                  * PASSTHROUGH - to add new attributes -- do NOT add break
297                  */
298                 case LDAP_MOD_ADD:
299                 case SLAP_MOD_SOFTADD:
300 add_only:;
301                         if ( at->bam_add_proc == NULL ) {
302                                 Debug( LDAP_DEBUG_TRACE,
303                                         "   backsql_modify_internal(): "
304                                         "add procedure is not defined "
305                                         "for attribute \"%s\"\n",
306                                         at->bam_ad->ad_cname.bv_val, 0, 0 );
307
308                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
309                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
310                                         rs->sr_text = "operation not permitted "
311                                                 "within namingContext";
312                                         goto done;
313                                 }
314
315                                 break;
316                         }
317                         
318                         Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
319                                 "adding new values for attribute \"%s\"\n",
320                                 at->bam_ad->ad_cname.bv_val, 0, 0 );
321                         for ( i = 0, at_val = c_mod->sm_values;
322                                         at_val->bv_val != NULL; 
323                                         i++, at_val++ ) {
324                                 if ( BACKSQL_IS_ADD( at->bam_expect_return ) ) {
325                                         pno = 1;
326                                         SQLBindParameter( sth, 1,
327                                                 SQL_PARAM_OUTPUT,
328                                                 SQL_C_ULONG, SQL_INTEGER,
329                                                 0, 0, &prc, 0, 0);
330                                 } else {
331                                         pno = 0;
332                                 }
333                                 po = ( BACKSQL_IS_ADD( at->bam_param_order ) ) > 0;
334 #ifdef BACKSQL_ARBITRARY_KEY
335                                 SQLBindParameter( sth, pno + 1 + po,
336                                         SQL_PARAM_INPUT, 
337                                         SQL_C_CHAR, SQL_VARCHAR,
338                                         0, 0, e_id->eid_keyval.bv_val, 0, 0 );
339 #else /* ! BACKSQL_ARBITRARY_KEY */
340                                 SQLBindParameter( sth, pno + 1 + po,
341                                         SQL_PARAM_INPUT, 
342                                         SQL_C_ULONG, SQL_INTEGER,
343                                         0, 0, &e_id->eid_keyval, 0, 0 );
344 #endif /* ! BACKSQL_ARBITRARY_KEY */
345
346                                 /*
347                                  * check for syntax needed here
348                                  * maybe need binary bind?
349                                  */
350                                 SQLBindParameter( sth, pno + 2 - po,
351                                         SQL_PARAM_INPUT,
352                                         SQL_C_CHAR, SQL_CHAR,
353                                         0, 0, at_val->bv_val, 
354                                         at_val->bv_len, 0 );
355
356                                 Debug( LDAP_DEBUG_TRACE,
357                                         "   backsql_modify_internal(): "
358                                         "executing \"%s\"\n", 
359                                         at->bam_add_proc, 0, 0 );
360                                 rc = SQLExecDirect( sth, at->bam_add_proc, 
361                                                 SQL_NTS );
362                                 if ( rc != SQL_SUCCESS ) {
363                                         Debug( LDAP_DEBUG_TRACE,
364                                                 "   backsql_modify_internal(): "
365                                                 "add_proc execution failed\n",
366                                                 0, 0, 0 );
367                                         backsql_PrintErrors( bi->db_env,
368                                                         dbh, sth, rc );
369
370                                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
371                                                 rs->sr_err = LDAP_OTHER;
372                                                 rs->sr_text = "SQL-backend error";
373                                                 goto done;
374                                         }
375                                 }
376 #ifdef BACKSQL_REALLOC_STMT
377                                 SQLFreeStmt( sth, SQL_DROP );
378                                 SQLAllocStmt( dbh, &sth );
379 #endif /* BACKSQL_REALLOC_STMT */
380                         }
381                         break;
382                         
383                 case LDAP_MOD_DELETE:
384                         if ( at->bam_delete_proc == NULL ) {
385                                 Debug( LDAP_DEBUG_TRACE,
386                                         "   backsql_modify_internal(): "
387                                         "delete procedure is not defined "
388                                         "for attribute \"%s\"\n",
389                                         at->bam_ad->ad_cname.bv_val, 0, 0 );
390
391                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
392                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
393                                         rs->sr_text = "operation not permitted "
394                                                 "within namingContext";
395                                         goto done;
396                                 }
397
398                                 break;
399                         }
400
401                         if ( c_mod->sm_values == NULL ) {
402                                 Debug( LDAP_DEBUG_TRACE,
403                                         "   backsql_modify_internal(): "
404                                         "no values given to delete "
405                                         "for attribute \"%s\" "
406                                         "-- deleting all values\n",
407                                         at->bam_ad->ad_cname.bv_val, 0, 0 );
408                                 goto del_all;
409                         }
410
411                         Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
412                                 "deleting values for attribute \"%s\"\n",
413                                 at->bam_ad->ad_cname.bv_val, 0, 0 );
414
415                         for ( i = 0, at_val = c_mod->sm_values;
416                                         at_val->bv_val != NULL;
417                                         i++, at_val++ ) {
418                                 if ( BACKSQL_IS_DEL( at->bam_expect_return ) ) {
419                                         pno = 1;
420                                         SQLBindParameter( sth, 1,
421                                                 SQL_PARAM_OUTPUT,
422                                                 SQL_C_ULONG, SQL_INTEGER,
423                                                 0, 0, &prc, 0, 0 );
424                                 } else {
425                                         pno = 0;
426                                 }
427                                 po = ( BACKSQL_IS_DEL( at->bam_param_order ) ) > 0;
428 #ifdef BACKSQL_ARBITRARY_KEY
429                                 SQLBindParameter( sth, pno + 1 + po,
430                                         SQL_PARAM_INPUT, 
431                                         SQL_C_CHAR, SQL_VARCHAR,
432                                         0, 0, e_id->eid_keyval.bv_val, 0, 0 );
433 #else /* ! BACKSQL_ARBITRARY_KEY */
434                                 SQLBindParameter( sth, pno + 1 + po,
435                                         SQL_PARAM_INPUT, 
436                                         SQL_C_ULONG, SQL_INTEGER,
437                                         0, 0, &e_id->eid_keyval, 0, 0 );
438 #endif /* ! BACKSQL_ARBITRARY_KEY */
439
440                                 /*
441                                  * check for syntax needed here 
442                                  * maybe need binary bind?
443                                  */
444                                 SQLBindParameter( sth, pno + 2 - po,
445                                         SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
446                                         0, 0, at_val->bv_val, 
447                                         at_val->bv_len, 0 );
448
449                                 Debug( LDAP_DEBUG_TRACE,
450                                         "   backsql_modify_internal(): "
451                                         "executing \"%s\"\n", 
452                                         at->bam_delete_proc, 0, 0 );
453                                 rc = SQLExecDirect( sth, at->bam_delete_proc,
454                                                 SQL_NTS );
455                                 if ( rc != SQL_SUCCESS ) {
456                                         Debug( LDAP_DEBUG_TRACE,
457                                                 "   backsql_modify_internal(): "
458                                                 "delete_proc execution "
459                                                 "failed\n", 0, 0, 0 );
460                                         backsql_PrintErrors( bi->db_env,
461                                                         dbh, sth, rc );
462
463                                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
464                                                 rs->sr_err = LDAP_OTHER;
465                                                 rs->sr_text = "SQL-backend error";
466                                                 goto done;
467                                         }
468                                 }
469 #ifdef BACKSQL_REALLOC_STMT
470                                 SQLFreeStmt( sth, SQL_DROP );
471                                 SQLAllocStmt( dbh, &sth );
472 #endif /* BACKSQL_REALLOC_STMT */
473                         }
474                         break;
475                 }
476 #ifndef BACKSQL_REALLOC_STMT
477                 SQLFreeStmt( sth, SQL_RESET_PARAMS );
478 #else /* BACKSQL_REALLOC_STMT */
479                 SQLFreeStmt( sth, SQL_DROP );
480 #endif /* BACKSQL_REALLOC_STMT */
481         }
482
483 done:;
484         
485 #ifndef BACKSQL_REALLOC_STMT
486         SQLFreeStmt( sth, SQL_DROP );
487 #endif /* BACKSQL_REALLOC_STMT */
488
489         Debug( LDAP_DEBUG_TRACE, "<==backsql_modify_internal(): %d%d%s\n",
490                 rs->sr_err, rs->sr_text ? ": " : "",
491                 rs->sr_text ? rs->sr_text : "" );
492
493         /*
494          * FIXME: should fail in case one change fails?
495          */
496         return rs->sr_err;
497 }
498
499 int
500 backsql_add( Operation *op, SlapReply *rs )
501 {
502         backsql_info            *bi = (backsql_info*)op->o_bd->be_private;
503         SQLHDBC                 dbh;
504         SQLHSTMT                sth;
505         unsigned long           new_keyval = 0;
506         long                    i;
507         RETCODE                 rc;
508         backsql_oc_map_rec      *oc = NULL;
509         backsql_at_map_rec      *at_rec = NULL;
510         backsql_entryID         parent_id = BACKSQL_ENTRYID_INIT;
511         Entry                   p;
512         Attribute               *at;
513         struct berval           *at_val;
514         struct berval           pdn;
515         /* first parameter #, parameter order */
516         SQLUSMALLINT            pno, po;
517         /* procedure return code */
518         int                     prc;
519         struct berval           realdn, realpdn;
520
521         Debug( LDAP_DEBUG_TRACE, "==>backsql_add(): adding entry \"%s\"\n",
522                         op->oq_add.rs_e->e_name.bv_val, 0, 0 );
523
524         /* check schema */
525         if ( global_schemacheck ) {
526                 char            textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };
527
528                 rs->sr_err = entry_schema_check( op->o_bd, op->oq_add.rs_e,
529                                 NULL,
530                                 &rs->sr_text, textbuf, sizeof( textbuf ) );
531                 if ( rs->sr_err != LDAP_SUCCESS ) {
532                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
533                                 "entry failed schema check -- aborting\n",
534                                 0, 0, 0 );
535                         goto done;
536                 }
537         }
538
539         /* search structural objectClass */
540         for ( at = op->oq_add.rs_e->e_attrs; at != NULL; at = at->a_next ) {
541                 if ( at->a_desc == slap_schema.si_ad_structuralObjectClass ) {
542                         break;
543                 }
544         }
545
546         /* there must exist */
547         assert( at != NULL );
548
549         /* I guess we should play with sub/supertypes to find a suitable oc */
550         oc = backsql_name2oc( bi, &at->a_vals[0] );
551
552         if ( oc == NULL ) {
553                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
554                         "cannot determine objectclass of entry -- aborting\n",
555                         0, 0, 0 );
556                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
557                 rs->sr_text = "operation not permitted within namingContext";
558                 goto done;
559         }
560
561         if ( oc->bom_create_proc == NULL ) {
562                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
563                         "create procedure is not defined for this objectclass "
564                         "- aborting\n", 0, 0, 0 );
565                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
566                 rs->sr_text = "operation not permitted within namingContext";
567                 goto done;
568
569         } else if ( BACKSQL_CREATE_NEEDS_SELECT( bi )
570                         && oc->bom_create_keyval == NULL ) {
571                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
572                         "create procedure needs select procedure, "
573                         "but none is defined - aborting\n", 0, 0, 0 );
574                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
575                 rs->sr_text = "operation not permitted within namingContext";
576                 goto done;
577         }
578
579         rs->sr_err = backsql_get_db_conn( op, &dbh );
580         if ( rs->sr_err != LDAP_SUCCESS ) {
581                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
582                         "could not get connection handle - exiting\n", 
583                         0, 0, 0 );
584                 rs->sr_text = ( rs->sr_err == LDAP_OTHER )
585                         ?  "SQL-backend error" : NULL;
586                 goto done;
587         }
588
589         /*
590          * Check if entry exists
591          */
592         realdn = op->oq_add.rs_e->e_name;
593         if ( backsql_api_dn2odbc( op, rs, &realdn ) ) {
594                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
595                         "backsql_api_dn2odbc failed\n", 
596                         0, 0, 0 );
597                 rs->sr_err = LDAP_OTHER;
598                 rs->sr_text = "SQL-backend error";
599                 goto done;
600         }
601
602         rs->sr_err = backsql_dn2id( bi, NULL, dbh, &realdn );
603         if ( rs->sr_err == LDAP_SUCCESS ) {
604                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
605                         "entry \"%s\" exists\n",
606                         op->oq_add.rs_e->e_name.bv_val, 0, 0 );
607                 rs->sr_err = LDAP_ALREADY_EXISTS;
608                 goto done;
609         }
610
611         /*
612          * Check if parent exists
613          */
614         dnParent( &op->oq_add.rs_e->e_name, &pdn );
615         realpdn = pdn;
616         if ( backsql_api_dn2odbc( op, rs, &realpdn ) ) {
617                 Debug( LDAP_DEBUG_TRACE, "backsql_search(): "
618                         "backsql_api_dn2odbc failed\n", 
619                         0, 0, 0 );
620                 rs->sr_err = LDAP_OTHER;
621                 rs->sr_text = "SQL-backend error";
622                 goto done;
623         }
624
625         rs->sr_err = backsql_dn2id( bi, &parent_id, dbh, &realpdn );
626         if ( rs->sr_err != LDAP_SUCCESS ) {
627                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
628                         "could not lookup parent entry for new record \"%s\"\n",
629                         pdn.bv_val, 0, 0 );
630
631                 if ( rs->sr_err != LDAP_NO_SUCH_OBJECT ) {
632                         goto done;
633                 }
634
635                 /*
636                  * Look for matched
637                  */
638                 while ( 1 ) {
639                         struct berval   dn;
640                         char            *matched = NULL;
641
642                         if ( realpdn.bv_val != pdn.bv_val ) {
643                                 ch_free( realpdn.bv_val );
644                         }
645
646                         dn = pdn;
647                         dnParent( &dn, &pdn );
648
649                         /*
650                          * Empty DN ("") defaults to LDAP_SUCCESS
651                          */
652                         realpdn = pdn;
653                         if ( backsql_api_dn2odbc( op, rs, &realpdn ) ) {
654                                 Debug( LDAP_DEBUG_TRACE, "backsql_add(): "
655                                         "backsql_api_dn2odbc failed\n", 
656                                         0, 0, 0 );
657                                 rs->sr_err = LDAP_OTHER;
658                                 rs->sr_text = "SQL-backend error";
659                                 goto done;
660                         }
661
662                         rs->sr_err = backsql_dn2id( bi, NULL, dbh, &realpdn );
663                         switch ( rs->sr_err ) {
664                         case LDAP_NO_SUCH_OBJECT:
665                                 if ( pdn.bv_len > 0 ) {
666                                         break;
667                                 }
668                                 /* fail over to next case */
669                                 
670                         case LDAP_SUCCESS:
671                                 matched = pdn.bv_val;
672                                 /* fail over to next case */
673
674                         default:
675                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
676                                 rs->sr_matched = matched;
677                                 goto done;
678                         } 
679                 }
680         }
681
682         /*
683          * create_proc is executed; if expect_return is set, then
684          * an output parameter is bound, which should contain 
685          * the id of the added row; otherwise the procedure
686          * is expected to return the id as the first column of a select
687          */
688
689         p.e_attrs = NULL;
690         p.e_name = pdn;
691         dnParent( &op->oq_add.rs_e->e_nname, &p.e_nname );
692         if ( !access_allowed( op, &p, slap_schema.si_ad_children,
693                                 NULL, ACL_WRITE, NULL ) ) {
694                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
695                 goto done;
696         }
697
698         rc = SQLAllocStmt( dbh, &sth );
699         if ( rc != SQL_SUCCESS ) {
700                 rs->sr_err = LDAP_OTHER;
701                 rs->sr_text = "SQL-backend error";
702                 goto done;
703         }
704
705         if ( BACKSQL_IS_ADD( oc->bom_expect_return ) ) {
706                 SQLBindParameter( sth, 1, SQL_PARAM_OUTPUT, SQL_C_ULONG, 
707                                 SQL_INTEGER, 0, 0, &new_keyval, 0, 0 );
708         }
709
710         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): executing \"%s\"\n",
711                 oc->bom_create_proc, 0, 0 );
712         rc = SQLExecDirect( sth, oc->bom_create_proc, SQL_NTS );
713         if ( rc != SQL_SUCCESS ) {
714                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
715                         "create_proc execution failed\n", 0, 0, 0 );
716                 backsql_PrintErrors( bi->db_env, dbh, sth, rc);
717                 SQLFreeStmt( sth, SQL_DROP );
718                 rs->sr_err = LDAP_OTHER;
719                 rs->sr_text = "SQL-backend error";
720                 goto done;
721         }
722         if ( op->o_noop ) {
723                 SQLTransact( SQL_NULL_HENV, dbh, SQL_ROLLBACK );
724         }
725
726         if ( !BACKSQL_IS_ADD( oc->bom_expect_return ) ) {
727                 SWORD           ncols;
728                 SQLINTEGER      value_len;
729
730                 if ( BACKSQL_CREATE_NEEDS_SELECT( bi ) ) {
731 #ifndef BACKSQL_REALLOC_STMT
732                         SQLFreeStmt( sth, SQL_RESET_PARAMS );
733 #else /* BACKSQL_REALLOC_STMT */
734                         SQLFreeStmt( sth, SQL_DROP );
735                         rc = SQLAllocStmt( dbh, &sth );
736                         if ( rc != SQL_SUCCESS ) {
737                                 rs->sr_err = LDAP_OTHER;
738                                 rs->sr_text = "SQL-backend error";
739                                 goto done;
740                         }
741 #endif /* BACKSQL_REALLOC_STMT */
742
743                         rc = SQLExecDirect( sth, oc->bom_create_keyval, SQL_NTS );
744                         if ( rc != SQL_SUCCESS ) {
745                                 rs->sr_err = LDAP_OTHER;
746                                 rs->sr_text = "SQL-backend error";
747                                 goto done;
748                         }
749                 }
750
751                 /*
752                  * the query to know the id of the inserted entry
753                  * must be embedded in the create procedure
754                  */
755                 rc = SQLNumResultCols( sth, &ncols );
756                 if ( rc != SQL_SUCCESS ) {
757                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
758                                 "create_proc result evaluation failed\n",
759                                 0, 0, 0 );
760                         backsql_PrintErrors( bi->db_env, dbh, sth, rc);
761                         SQLFreeStmt( sth, SQL_DROP );
762                         rs->sr_err = LDAP_OTHER;
763                         rs->sr_text = "SQL-backend error";
764                         goto done;
765
766                 } else if ( ncols != 1 ) {
767                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
768                                 "create_proc result is bogus (ncols=%d)\n",
769                                 ncols, 0, 0 );
770                         backsql_PrintErrors( bi->db_env, dbh, sth, rc);
771                         SQLFreeStmt( sth, SQL_DROP );
772                         rs->sr_err = LDAP_OTHER;
773                         rs->sr_text = "SQL-backend error";
774                         goto done;
775                 }
776
777 #if 0
778                 {
779                         SQLCHAR         colname[ 64 ];
780                         SQLSMALLINT     name_len, col_type, col_scale, col_null;
781                         UDWORD          col_prec;
782
783                         /*
784                          * FIXME: check whether col_type is compatible,
785                          * if it can be null and so on ...
786                          */
787                         rc = SQLDescribeCol( sth, (SQLUSMALLINT)1, 
788                                         &colname[ 0 ], 
789                                         (SQLUINTEGER)( sizeof( colname ) - 1 ),
790                                         &name_len, &col_type,
791                                         &col_prec, &col_scale, &col_null );
792                 }
793 #endif
794
795                 rc = SQLBindCol( sth, (SQLUSMALLINT)1, SQL_C_ULONG,
796                                 (SQLPOINTER)&new_keyval, 
797                                 (SQLINTEGER)sizeof( new_keyval ), 
798                                 &value_len );
799
800                 rc = SQLFetch( sth );
801
802                 if ( value_len <= 0 ) {
803                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
804                                 "create_proc result is empty?\n",
805                                 0, 0, 0 );
806                         backsql_PrintErrors( bi->db_env, dbh, sth, rc);
807                         SQLFreeStmt( sth, SQL_DROP );
808                         rs->sr_err = LDAP_OTHER;
809                         rs->sr_text = "SQL-backend error";
810                         goto done;
811                 }
812         }
813
814 #ifndef BACKSQL_REALLOC_STMT
815         SQLFreeStmt( sth, SQL_RESET_PARAMS );
816 #else /* BACKSQL_REALLOC_STMT */
817         SQLFreeStmt( sth, SQL_DROP );
818 #endif /* BACKSQL_REALLOC_STMT */
819
820         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
821                 "create_proc returned keyval=%ld\n", new_keyval, 0, 0 );
822
823         for ( at = op->oq_add.rs_e->e_attrs; at != NULL; at = at->a_next ) {
824                 SQLUSMALLINT    currpos;
825
826                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
827                         "adding attribute \"%s\"\n", 
828                         at->a_desc->ad_cname.bv_val, 0, 0 );
829
830                 /*
831                  * Skip:
832                  * - the first occurrence of objectClass, which is used
833                  *   to determine how to bulid the SQL entry (FIXME ?!?)
834                  * - operational attributes
835                  *   empty attributes (FIXME ?!?)
836                  */
837                 if ( backsql_attr_skip( at->a_desc, at->a_vals ) ) {
838                         continue;
839                 }
840
841                 at_rec = backsql_ad2at( oc, at->a_desc ); 
842   
843                 if ( at_rec == NULL ) {
844                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
845                                 "attribute \"%s\" is not registered "
846                                 "in objectclass \"%s\"\n",
847                                 at->a_desc->ad_cname.bv_val,
848                                 BACKSQL_OC_NAME( oc ), 0 );
849
850                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
851                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
852                                 rs->sr_text = "operation not permitted "
853                                         "within namingContext";
854                                 goto done;
855                         }
856
857                         continue;
858                 }
859                 
860                 if ( at_rec->bam_add_proc == NULL ) {
861                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
862                                 "add procedure is not defined "
863                                 "for attribute \"%s\"\n",
864                                 at->a_desc->ad_cname.bv_val, 0, 0 );
865
866                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
867                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
868                                 rs->sr_text = "operation not permitted "
869                                         "within namingContext";
870                                 goto done;
871                         }
872
873                         continue;
874                 }
875
876 #ifdef BACKSQL_REALLOC_STMT
877                 rc = backsql_Prepare( dbh, &sth, at_rec->bam_add_proc, 0 );
878                 if ( rc != SQL_SUCCESS ) {
879
880                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
881                                 rs->sr_err = LDAP_OTHER;
882                                 rs->sr_text = "SQL-backend error";
883                                 goto done;
884                         }
885
886                         continue;
887                 }
888 #endif /* BACKSQL_REALLOC_STMT */
889
890                 if ( BACKSQL_IS_ADD( at_rec->bam_expect_return ) ) {
891                         pno = 1;
892                         SQLBindParameter( sth, 1, SQL_PARAM_OUTPUT,
893                                         SQL_C_ULONG, SQL_INTEGER,
894                                         0, 0, &prc, 0, 0 );
895                 } else {
896                         pno = 0;
897                 }
898
899                 po = ( BACKSQL_IS_ADD( at_rec->bam_param_order ) ) > 0;
900                 currpos = pno + 1 + po;
901                 SQLBindParameter( sth, currpos,
902                                 SQL_PARAM_INPUT, SQL_C_ULONG,
903                                 SQL_INTEGER, 0, 0, &new_keyval, 0, 0 );
904                 currpos = pno + 2 - po;
905
906                 for ( i = 0, at_val = &at->a_vals[ i ];
907                                 at_val->bv_val != NULL;
908                                 i++, at_val = &at->a_vals[ i ] ) {
909
910                         /*
911                          * Do not deal with the objectClass that is used
912                          * to build the entry
913                          */
914                         if ( at->a_desc == slap_schema.si_ad_objectClass ) {
915                                 if ( bvmatch( at_val, &oc->bom_oc->soc_cname ) ) {
916                                         continue;
917                                 }
918                         }
919
920                         /*
921                          * check for syntax needed here 
922                          * maybe need binary bind?
923                          */
924
925                         backsql_BindParamStr( sth, currpos,
926                                         at_val->bv_val, at_val->bv_len + 1 );
927 #ifdef SECURITY_PARANOID
928                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
929                                 "executing \"%s\", id=%ld\n", 
930                                 at_rec->bam_add_proc, new_keyval, 0 );
931 #else
932                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
933                                 "executing \"%s\" for val[%d], id=%ld\n", 
934                                 at_rec->bam_add_proc, i, new_keyval );
935 #endif
936 #ifndef BACKSQL_REALLOC_STMT
937                         rc = SQLExecDirect( sth, at_rec->bam_add_proc, SQL_NTS );
938 #else /* BACKSQL_REALLOC_STMT */
939                         rc = SQLExecute( sth );
940 #endif /* BACKSQL_REALLOC_STMT */
941                         if ( rc != SQL_SUCCESS ) {
942                                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
943                                         "add_proc execution failed\n", 
944                                         0, 0, 0 );
945                                 backsql_PrintErrors( bi->db_env, dbh, sth, rc );
946
947                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
948                                         rs->sr_err = LDAP_OTHER;
949                                         rs->sr_text = "SQL-backend error";
950                                         goto done;
951                                 }
952                         }
953                 }
954 #ifndef BACKSQL_REALLOC_STMT
955                 SQLFreeStmt( sth, SQL_RESET_PARAMS ); 
956 #else /* BACKSQL_REALLOC_STMT */
957                 SQLFreeStmt( sth, SQL_DROP );
958 #endif /* BACKSQL_REALLOC_STMT */
959         }
960
961 #ifdef BACKSQL_REALLOC_STMT
962         rc = backsql_Prepare( dbh, &sth, bi->insentry_query, 0 );
963         if ( rc != SQL_SUCCESS ) {
964                 rs->sr_err = LDAP_OTHER;
965                 rs->sr_text = "SQL-backend error";
966                 goto done;
967         }
968 #endif /* BACKSQL_REALLOC_STMT */
969         
970         backsql_BindParamStr( sth, 1, op->oq_add.rs_e->e_name.bv_val,
971                         BACKSQL_MAX_DN_LEN );
972         SQLBindParameter( sth, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
973                         0, 0, &oc->bom_id, 0, 0 );
974 #ifdef BACKSQL_ARBITRARY_KEY
975         SQLBindParameter( sth, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_VARCHAR,
976                         0, 0, parent_id.eid_id.bv_val, 0, 0 );
977 #else /* ! BACKSQL_ARBITRARY_KEY */
978         SQLBindParameter( sth, 3, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
979                         0, 0, &parent_id.eid_id, 0, 0 );
980 #endif /* ! BACKSQL_ARBITRARY_KEY */
981         SQLBindParameter( sth, 4, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
982                         0, 0, &new_keyval, 0, 0 );
983
984         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): executing \"%s\" for dn \"%s\"\n",
985                         bi->insentry_query, op->oq_add.rs_e->e_name.bv_val, 0 );
986 #ifdef BACKSQL_ARBITRARY_KEY
987         Debug( LDAP_DEBUG_TRACE, "                  for oc_map_id=%ld, "
988                         "parent_id=%s, keyval=%ld\n",
989                         oc->bom_id, parent_id.eid_id.bv_val, new_keyval );
990 #else /* ! BACKSQL_ARBITRARY_KEY */
991         Debug( LDAP_DEBUG_TRACE, "                  for oc_map_id=%ld, "
992                         "parent_id=%ld, keyval=%ld\n",
993                         oc->bom_id, parent_id.eid_id, new_keyval );
994 #endif /* ! BACKSQL_ARBITRARY_KEY */
995 #ifndef BACKSQL_REALLOC_STMT
996         rc = SQLExecDirect( sth, bi->insentry_query, SQL_NTS );
997 #else /* BACKSQL_REALLOC_STMT */
998         rc = SQLExecute( sth );
999 #endif /* BACKSQL_REALLOC_STMT */
1000         if ( rc != SQL_SUCCESS ) {
1001                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
1002                         "could not insert ldap_entries record\n", 0, 0, 0 );
1003                 backsql_PrintErrors( bi->db_env, dbh, sth, rc );
1004                 
1005                 /*
1006                  * execute delete_proc to delete data added !!!
1007                  */
1008                 SQLFreeStmt( sth, SQL_DROP );
1009                 rs->sr_err = LDAP_OTHER;
1010                 rs->sr_text = "SQL-backend error";
1011                 goto done;
1012         }
1013         
1014         SQLFreeStmt( sth, SQL_DROP );
1015
1016         /*
1017          * Commit only if all operations succeed
1018          */
1019         SQLTransact( SQL_NULL_HENV, dbh, 
1020                         op->o_noop ? SQL_ROLLBACK : SQL_COMMIT );
1021
1022         /*
1023          * FIXME: NOOP does not work for add -- it works for all 
1024          * the other operations, and I don't get the reason :(
1025          * 
1026          * hint: there might be some autocommit in Postgres
1027          * so that when the unique id of the key table is
1028          * automatically increased, there's no rollback.
1029          * We might implement a "rollback" procedure consisting
1030          * in deleting that row.
1031          */
1032
1033 done:;
1034         send_ldap_result( op, rs );
1035
1036         if ( realdn.bv_val != op->oq_add.rs_e->e_name.bv_val ) {
1037                 ch_free( realdn.bv_val );
1038         }
1039         if ( realpdn.bv_val != pdn.bv_val ) {
1040                 ch_free( realpdn.bv_val );
1041         }
1042         if ( parent_id.eid_dn.bv_val != NULL ) {
1043                 backsql_free_entryID( &parent_id, 0 );
1044         }
1045
1046         Debug( LDAP_DEBUG_TRACE, "<==backsql_add(): %d%s%s\n",
1047                         rs->sr_err,
1048                         rs->sr_text ? ": " : "",
1049                         rs->sr_text ? rs->sr_text : "" );
1050
1051         return ( ( rs->sr_err == LDAP_SUCCESS ) ? op->o_noop : 1 );
1052 }
1053
1054 #endif /* SLAPD_SQL */
1055