]> git.sur5r.net Git - openldap/blob - servers/slapd/back-sql/add.c
Consolidated static/dynamic backend switches
[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_bvalues ) ) {
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                         rc = backsql_BindParamID( asth, 1, &e_id->keyval );
183                         if ( rc != SQL_SUCCESS ) {
184                                 Debug( LDAP_DEBUG_TRACE,
185                                         "   backsql_modify_internal(): "
186                                         "error binding key value parameter\n",
187                                         0, 0, 0 );
188                                 backsql_PrintErrors( bi->db_env, dbh, 
189                                                 asth, rc );
190                                 SQLFreeStmt( asth, SQL_DROP );
191
192                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
193                                         rs->sr_err = LDAP_OTHER;
194                                         rs->sr_text = "SQL-backend error";
195                                         goto done;
196                                 }
197
198                                 break;
199                         }
200                         
201                         rc = SQLExecute( asth );
202                         if ( !BACKSQL_SUCCESS( rc ) ) {
203                                 Debug( LDAP_DEBUG_TRACE,
204                                         "   backsql_modify_internal(): "
205                                         "error executing attribute query\n",
206                                         0, 0, 0 );
207                                 backsql_PrintErrors( bi->db_env, dbh, 
208                                                 asth, rc );
209                                 SQLFreeStmt( asth, SQL_DROP );
210
211                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
212                                         rs->sr_err = LDAP_OTHER;
213                                         rs->sr_text = "SQL-backend error";
214                                         goto done;
215                                 }
216
217                                 break;
218                         }
219
220                         backsql_BindRowAsStrings( asth, &row );
221                         rc = SQLFetch( asth );
222                         for ( ; BACKSQL_SUCCESS( rc ); rc = SQLFetch( asth ) ) {
223                                 for ( i = 0; i < row.ncols; i++ ) {
224                                         if ( BACKSQL_IS_DEL( at->bam_expect_return ) ) {
225                                                 pno = 1;
226                                                 SQLBindParameter(sth, 1,
227                                                         SQL_PARAM_OUTPUT,
228                                                         SQL_C_ULONG,
229                                                         SQL_INTEGER,
230                                                         0, 0, &prc, 0, 0 );
231                                         } else {
232                                                 pno = 0;
233                                         }
234                                         po = ( BACKSQL_IS_DEL( at->bam_param_order ) ) > 0;
235                                         SQLBindParameter( sth, pno + 1 + po,
236                                                 SQL_PARAM_INPUT,
237                                                 SQL_C_ULONG, SQL_INTEGER,
238                                                 0, 0, &e_id->keyval, 0, 0 );
239
240                                         /*
241                                          * check for syntax needed here 
242                                          * maybe need binary bind?
243                                          */
244                                         SQLBindParameter(sth, pno + 2 - po,
245                                                 SQL_PARAM_INPUT,
246                                                 SQL_C_CHAR, SQL_CHAR,
247                                                 0, 0, row.cols[ i ],
248                                                 strlen( row.cols[ i ] ), 0 );
249                          
250                                         Debug( LDAP_DEBUG_TRACE, 
251                                                 "   backsql_modify_internal(): "
252                                                 "executing \"%s\"\n",
253                                                 at->bam_delete_proc, 0, 0 );
254                                         rc = SQLExecDirect( sth,
255                                                 at->bam_delete_proc, SQL_NTS );
256                                         if ( rc != SQL_SUCCESS ) {
257                                                 Debug( LDAP_DEBUG_TRACE,
258                                                         "   backsql_modify_internal(): "
259                                                         "delete_proc "
260                                                         "execution failed\n",
261                                                         0, 0, 0 );
262                                                 backsql_PrintErrors( bi->db_env,
263                                                                 dbh, sth, rc );
264
265                                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
266                                                         rs->sr_err = LDAP_OTHER;
267                                                         rs->sr_text = "SQL-backend error";
268                                                         goto done;
269                                                 }
270                                         }
271 #ifdef BACKSQL_REALLOC_STMT
272                                         SQLFreeStmt( sth, SQL_DROP );
273                                         SQLAllocStmt( dbh, &sth );
274 #endif /* BACKSQL_REALLOC_STMT */
275                                 }
276                         }
277                         backsql_FreeRow( &row );
278                         SQLFreeStmt( asth, SQL_DROP );
279                 }
280
281                 /*
282                  * PASSTHROUGH - to add new attributes -- do NOT add break
283                  */
284                 case LDAP_MOD_ADD:
285                 case SLAP_MOD_SOFTADD:
286 add_only:;
287                         if ( at->bam_add_proc == NULL ) {
288                                 Debug( LDAP_DEBUG_TRACE,
289                                         "   backsql_modify_internal(): "
290                                         "add procedure is not defined "
291                                         "for attribute \"%s\"\n",
292                                         at->bam_ad->ad_cname.bv_val, 0, 0 );
293
294                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
295                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
296                                         rs->sr_text = "operation not permitted "
297                                                 "within namingContext";
298                                         goto done;
299                                 }
300
301                                 break;
302                         }
303                         
304                         Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
305                                 "adding new values for attribute \"%s\"\n",
306                                 at->bam_ad->ad_cname.bv_val, 0, 0 );
307                         for ( i = 0, at_val = c_mod->sm_bvalues;
308                                         at_val->bv_val != NULL; 
309                                         i++, at_val++ ) {
310                                 if ( BACKSQL_IS_ADD( at->bam_expect_return ) ) {
311                                         pno = 1;
312                                         SQLBindParameter( sth, 1,
313                                                 SQL_PARAM_OUTPUT,
314                                                 SQL_C_ULONG, SQL_INTEGER,
315                                                 0, 0, &prc, 0, 0);
316                                 } else {
317                                         pno = 0;
318                                 }
319                                 po = ( BACKSQL_IS_ADD( at->bam_param_order ) ) > 0;
320                                 SQLBindParameter( sth, pno + 1 + po,
321                                         SQL_PARAM_INPUT, 
322                                         SQL_C_ULONG, SQL_INTEGER,
323                                         0, 0, &e_id->keyval, 0, 0 );
324
325                                 /*
326                                  * check for syntax needed here
327                                  * maybe need binary bind?
328                                  */
329                                 SQLBindParameter( sth, pno + 2 - po,
330                                         SQL_PARAM_INPUT,
331                                         SQL_C_CHAR, SQL_CHAR,
332                                         0, 0, at_val->bv_val, 
333                                         at_val->bv_len, 0 );
334
335                                 Debug( LDAP_DEBUG_TRACE,
336                                         "   backsql_modify_internal(): "
337                                         "executing \"%s\"\n", 
338                                         at->bam_add_proc, 0, 0 );
339                                 rc = SQLExecDirect( sth, at->bam_add_proc, 
340                                                 SQL_NTS );
341                                 if ( rc != SQL_SUCCESS ) {
342                                         Debug( LDAP_DEBUG_TRACE,
343                                                 "   backsql_modify_internal(): "
344                                                 "add_proc execution failed\n",
345                                                 0, 0, 0 );
346                                         backsql_PrintErrors( bi->db_env,
347                                                         dbh, sth, rc );
348
349                                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
350                                                 rs->sr_err = LDAP_OTHER;
351                                                 rs->sr_text = "SQL-backend error";
352                                                 goto done;
353                                         }
354                                 }
355 #ifdef BACKSQL_REALLOC_STMT
356                                 SQLFreeStmt( sth, SQL_DROP );
357                                 SQLAllocStmt( dbh, &sth );
358 #endif /* BACKSQL_REALLOC_STMT */
359                         }
360                         break;
361                         
362                 case LDAP_MOD_DELETE:
363                         if ( at->bam_delete_proc == NULL ) {
364                                 Debug( LDAP_DEBUG_TRACE,
365                                         "   backsql_modify_internal(): "
366                                         "delete procedure is not defined "
367                                         "for attribute \"%s\"\n",
368                                         at->bam_ad->ad_cname.bv_val, 0, 0 );
369
370                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
371                                         rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
372                                         rs->sr_text = "operation not permitted "
373                                                 "within namingContext";
374                                         goto done;
375                                 }
376
377                                 break;
378                         }
379
380                         if ( c_mod->sm_bvalues == NULL ) {
381                                 Debug( LDAP_DEBUG_TRACE,
382                                         "   backsql_modify_internal(): "
383                                         "no values given to delete "
384                                         "for attribute \"%s\" "
385                                         "-- deleting all values\n",
386                                         at->bam_ad->ad_cname.bv_val, 0, 0 );
387                                 goto del_all;
388                         }
389
390                         Debug( LDAP_DEBUG_TRACE, "   backsql_modify_internal(): "
391                                 "deleting values for attribute \"%s\"\n",
392                                 at->bam_ad->ad_cname.bv_val, 0, 0 );
393
394                         for ( i = 0, at_val = c_mod->sm_bvalues;
395                                         at_val->bv_val != NULL;
396                                         i++, at_val++ ) {
397                                 if ( BACKSQL_IS_DEL( at->bam_expect_return ) ) {
398                                         pno = 1;
399                                         SQLBindParameter( sth, 1,
400                                                 SQL_PARAM_OUTPUT,
401                                                 SQL_C_ULONG, SQL_INTEGER,
402                                                 0, 0, &prc, 0, 0 );
403                                 } else {
404                                         pno = 0;
405                                 }
406                                 po = ( BACKSQL_IS_DEL( at->bam_param_order ) ) > 0;
407                                 SQLBindParameter( sth, pno + 1 + po,
408                                         SQL_PARAM_INPUT, 
409                                         SQL_C_ULONG, SQL_INTEGER,
410                                         0, 0, &e_id->keyval, 0, 0 );
411
412                                 /*
413                                  * check for syntax needed here 
414                                  * maybe need binary bind?
415                                  */
416                                 SQLBindParameter( sth, pno + 2 - po,
417                                         SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
418                                         0, 0, at_val->bv_val, 
419                                         at_val->bv_len, 0 );
420
421                                 Debug( LDAP_DEBUG_TRACE,
422                                         "   backsql_modify_internal(): "
423                                         "executing \"%s\"\n", 
424                                         at->bam_delete_proc, 0, 0 );
425                                 rc = SQLExecDirect( sth, at->bam_delete_proc,
426                                                 SQL_NTS );
427                                 if ( rc != SQL_SUCCESS ) {
428                                         Debug( LDAP_DEBUG_TRACE,
429                                                 "   backsql_modify_internal(): "
430                                                 "delete_proc execution "
431                                                 "failed\n", 0, 0, 0 );
432                                         backsql_PrintErrors( bi->db_env,
433                                                         dbh, sth, rc );
434
435                                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
436                                                 rs->sr_err = LDAP_OTHER;
437                                                 rs->sr_text = "SQL-backend error";
438                                                 goto done;
439                                         }
440                                 }
441 #ifdef BACKSQL_REALLOC_STMT
442                                 SQLFreeStmt( sth, SQL_DROP );
443                                 SQLAllocStmt( dbh, &sth );
444 #endif /* BACKSQL_REALLOC_STMT */
445                         }
446                         break;
447                 }
448 #ifndef BACKSQL_REALLOC_STMT
449                 SQLFreeStmt( sth, SQL_RESET_PARAMS );
450 #else /* BACKSQL_REALLOC_STMT */
451                 SQLFreeStmt( sth, SQL_DROP );
452 #endif /* BACKSQL_REALLOC_STMT */
453         }
454
455 done:;
456         
457 #ifndef BACKSQL_REALLOC_STMT
458         SQLFreeStmt( sth, SQL_DROP );
459 #endif /* BACKSQL_REALLOC_STMT */
460
461         Debug( LDAP_DEBUG_TRACE, "<==backsql_modify_internal(): %d%d%s\n",
462                 rs->sr_err, rs->sr_text ? ": " : "",
463                 rs->sr_text ? rs->sr_text : "" );
464
465         /*
466          * FIXME: should fail in case one change fails?
467          */
468         return rs->sr_err;
469 }
470
471 int
472 backsql_add( Operation *op, SlapReply *rs )
473 {
474         backsql_info            *bi = (backsql_info*)op->o_bd->be_private;
475         SQLHDBC                 dbh;
476         SQLHSTMT                sth;
477         unsigned long           new_keyval = 0;
478         long                    i;
479         RETCODE                 rc;
480         backsql_oc_map_rec      *oc = NULL;
481         backsql_at_map_rec      *at_rec = NULL;
482         backsql_entryID         e_id, parent_id;
483         Entry                   p;
484         Attribute               *at;
485         struct berval           *at_val;
486         struct berval           pdn;
487         /* first parameter #, parameter order */
488         SQLUSMALLINT            pno, po;
489         /* procedure return code */
490         int                     prc;
491
492         Debug( LDAP_DEBUG_TRACE, "==>backsql_add(): adding entry \"%s\"\n",
493                         op->oq_add.rs_e->e_name.bv_val, 0, 0 );
494
495         /* check schema */
496         if ( global_schemacheck ) {
497                 char            textbuf[ SLAP_TEXT_BUFLEN ] = { '\0' };
498
499                 rs->sr_err = entry_schema_check( op->o_bd, op->oq_add.rs_e,
500                                 NULL,
501                                 &rs->sr_text, textbuf, sizeof( textbuf ) );
502                 if ( rs->sr_err != LDAP_SUCCESS ) {
503                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
504                                 "entry failed schema check -- aborting\n",
505                                 0, 0, 0 );
506                         goto done;
507                 }
508         }
509
510         /* search structural objectClass */
511         for ( at = op->oq_add.rs_e->e_attrs; at != NULL; at = at->a_next ) {
512                 if ( at->a_desc == slap_schema.si_ad_structuralObjectClass ) {
513                         break;
514                 }
515         }
516
517         /* there must exist */
518         assert( at != NULL );
519
520         /* I guess we should play with sub/supertypes to find a suitable oc */
521         oc = backsql_name2oc( bi, &at->a_vals[0] );
522
523         if ( oc == NULL ) {
524                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
525                         "cannot determine objectclass of entry -- aborting\n",
526                         0, 0, 0 );
527                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
528                 rs->sr_text = "operation not permitted within namingContext";
529                 goto done;
530         }
531
532         if ( oc->bom_create_proc == NULL ) {
533                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
534                         "create procedure is not defined for this objectclass "
535                         "- aborting\n", 0, 0, 0 );
536                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
537                 rs->sr_text = "operation not permitted within namingContext";
538                 goto done;
539
540         } else if ( BACKSQL_CREATE_NEEDS_SELECT( bi )
541                         && oc->bom_create_keyval == NULL ) {
542                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
543                         "create procedure needs select procedure, "
544                         "but none is defined - aborting\n", 0, 0, 0 );
545                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
546                 rs->sr_text = "operation not permitted within namingContext";
547                 goto done;
548         }
549
550         rs->sr_err = backsql_get_db_conn( op, &dbh );
551         if ( rs->sr_err != LDAP_SUCCESS ) {
552                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
553                         "could not get connection handle - exiting\n", 
554                         0, 0, 0 );
555                 rs->sr_text = ( rs->sr_err == LDAP_OTHER )
556                         ?  "SQL-backend error" : NULL;
557                 goto done;
558         }
559
560         /*
561          * Check if entry exists
562          */
563         rs->sr_err = backsql_dn2id( bi, &e_id, dbh, &op->oq_add.rs_e->e_name );
564         if ( rs->sr_err == LDAP_SUCCESS ) {
565                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
566                         "entry \"%s\" exists\n",
567                         op->oq_add.rs_e->e_name.bv_val, 0, 0 );
568                 rs->sr_err = LDAP_ALREADY_EXISTS;
569                 goto done;
570         }
571
572         /*
573          * Check if parent exists
574          */
575         dnParent( &op->oq_add.rs_e->e_name, &pdn );
576         rs->sr_err = backsql_dn2id( bi, &parent_id, dbh, &pdn );
577         if ( rs->sr_err != LDAP_SUCCESS ) {
578                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
579                         "could not lookup parent entry for new record \"%s\"\n",
580                         pdn.bv_val, 0, 0 );
581
582                 if ( rs->sr_err != LDAP_NO_SUCH_OBJECT ) {
583                         goto done;
584                 }
585
586                 /*
587                  * Look for matched
588                  */
589                 while ( 1 ) {
590                         struct berval   dn;
591                         char            *matched = NULL;
592
593                         dn = pdn;
594                         dnParent( &dn, &pdn );
595
596                         /*
597                          * Empty DN ("") defaults to LDAP_SUCCESS
598                          */
599                         rs->sr_err = backsql_dn2id( bi, &parent_id, dbh, &pdn );
600                         switch ( rs->sr_err ) {
601                         case LDAP_NO_SUCH_OBJECT:
602                                 if ( pdn.bv_len > 0 ) {
603                                         break;
604                                 }
605                                 /* fail over to next case */
606                                 
607                         case LDAP_SUCCESS:
608                                 matched = pdn.bv_val;
609                                 /* fail over to next case */
610
611                         default:
612                                 rs->sr_err = LDAP_NO_SUCH_OBJECT;
613                                 rs->sr_matched = matched;
614                                 goto done;
615                         } 
616                 }
617         }
618
619         /*
620          * create_proc is executed; if expect_return is set, then
621          * an output parameter is bound, which should contain 
622          * the id of the added row; otherwise the procedure
623          * is expected to return the id as the first column of a select
624          */
625
626         p.e_attrs = NULL;
627         p.e_name = pdn;
628         dnParent( &op->oq_add.rs_e->e_nname, &p.e_nname );
629         if ( !access_allowed( op, &p, slap_schema.si_ad_children,
630                                 NULL, ACL_WRITE, NULL ) ) {
631                 rs->sr_err = LDAP_INSUFFICIENT_ACCESS;
632                 goto done;
633         }
634
635         rc = SQLAllocStmt( dbh, &sth );
636         if ( rc != SQL_SUCCESS ) {
637                 rs->sr_err = LDAP_OTHER;
638                 rs->sr_text = "SQL-backend error";
639                 goto done;
640         }
641
642         if ( BACKSQL_IS_ADD( oc->bom_expect_return ) ) {
643                 SQLBindParameter( sth, 1, SQL_PARAM_OUTPUT, SQL_C_ULONG, 
644                                 SQL_INTEGER, 0, 0, &new_keyval, 0, 0 );
645         }
646
647         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): executing \"%s\"\n",
648                 oc->bom_create_proc, 0, 0 );
649         rc = SQLExecDirect( sth, oc->bom_create_proc, SQL_NTS );
650         if ( rc != SQL_SUCCESS ) {
651                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
652                         "create_proc execution failed\n", 0, 0, 0 );
653                 backsql_PrintErrors( bi->db_env, dbh, sth, rc);
654                 SQLFreeStmt( sth, SQL_DROP );
655                 rs->sr_err = LDAP_OTHER;
656                 rs->sr_text = "SQL-backend error";
657                 goto done;
658         }
659         if ( op->o_noop ) {
660                 SQLTransact( SQL_NULL_HENV, dbh, SQL_ROLLBACK );
661         }
662
663         if ( !BACKSQL_IS_ADD( oc->bom_expect_return ) ) {
664                 SWORD           ncols;
665                 SQLINTEGER      value_len;
666
667                 if ( BACKSQL_CREATE_NEEDS_SELECT( bi ) ) {
668 #ifndef BACKSQL_REALLOC_STMT
669                         SQLFreeStmt( sth, SQL_RESET_PARAMS );
670 #else /* BACKSQL_REALLOC_STMT */
671                         SQLFreeStmt( sth, SQL_DROP );
672                         rc = SQLAllocStmt( dbh, &sth );
673                         if ( rc != SQL_SUCCESS ) {
674                                 rs->sr_err = LDAP_OTHER;
675                                 rs->sr_text = "SQL-backend error";
676                                 goto done;
677                         }
678 #endif /* BACKSQL_REALLOC_STMT */
679
680                         rc = SQLExecDirect( sth, oc->bom_create_keyval, SQL_NTS );
681                         if ( rc != SQL_SUCCESS ) {
682                                 rs->sr_err = LDAP_OTHER;
683                                 rs->sr_text = "SQL-backend error";
684                                 goto done;
685                         }
686                 }
687
688                 /*
689                  * the query to know the id of the inserted entry
690                  * must be embedded in the create procedure
691                  */
692                 rc = SQLNumResultCols( sth, &ncols );
693                 if ( rc != SQL_SUCCESS ) {
694                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
695                                 "create_proc result evaluation failed\n",
696                                 0, 0, 0 );
697                         backsql_PrintErrors( bi->db_env, dbh, sth, rc);
698                         SQLFreeStmt( sth, SQL_DROP );
699                         rs->sr_err = LDAP_OTHER;
700                         rs->sr_text = "SQL-backend error";
701                         goto done;
702
703                 } else if ( ncols != 1 ) {
704                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
705                                 "create_proc result is bogus (ncols=%d)\n",
706                                 ncols, 0, 0 );
707                         backsql_PrintErrors( bi->db_env, dbh, sth, rc);
708                         SQLFreeStmt( sth, SQL_DROP );
709                         rs->sr_err = LDAP_OTHER;
710                         rs->sr_text = "SQL-backend error";
711                         goto done;
712                 }
713
714 #if 0
715                 {
716                         SQLCHAR         colname[ 64 ];
717                         SQLSMALLINT     name_len, col_type, col_scale, col_null;
718                         UDWORD          col_prec;
719
720                         /*
721                          * FIXME: check whether col_type is compatible,
722                          * if it can be null and so on ...
723                          */
724                         rc = SQLDescribeCol( sth, (SQLUSMALLINT)1, 
725                                         &colname[ 0 ], 
726                                         (SQLUINTEGER)( sizeof( colname ) - 1 ),
727                                         &name_len, &col_type,
728                                         &col_prec, &col_scale, &col_null );
729                 }
730 #endif
731
732                 rc = SQLBindCol( sth, (SQLUSMALLINT)1, SQL_C_ULONG,
733                                 (SQLPOINTER)&new_keyval, 
734                                 (SQLINTEGER)sizeof( new_keyval ), 
735                                 &value_len );
736
737                 rc = SQLFetch( sth );
738
739                 if ( value_len <= 0 ) {
740                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
741                                 "create_proc result is empty?\n",
742                                 0, 0, 0 );
743                         backsql_PrintErrors( bi->db_env, dbh, sth, rc);
744                         SQLFreeStmt( sth, SQL_DROP );
745                         rs->sr_err = LDAP_OTHER;
746                         rs->sr_text = "SQL-backend error";
747                         goto done;
748                 }
749         }
750
751 #ifndef BACKSQL_REALLOC_STMT
752         SQLFreeStmt( sth, SQL_RESET_PARAMS );
753 #else /* BACKSQL_REALLOC_STMT */
754         SQLFreeStmt( sth, SQL_DROP );
755 #endif /* BACKSQL_REALLOC_STMT */
756
757         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
758                 "create_proc returned keyval=%ld\n", new_keyval, 0, 0 );
759
760         for ( at = op->oq_add.rs_e->e_attrs; at != NULL; at = at->a_next ) {
761                 SQLUSMALLINT    currpos;
762
763                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
764                         "adding attribute \"%s\"\n", 
765                         at->a_desc->ad_cname.bv_val, 0, 0 );
766
767                 /*
768                  * Skip:
769                  * - the first occurrence of objectClass, which is used
770                  *   to determine how to bulid the SQL entry (FIXME ?!?)
771                  * - operational attributes
772                  *   empty attributes (FIXME ?!?)
773                  */
774                 if ( backsql_attr_skip( at->a_desc, at->a_vals ) ) {
775                         continue;
776                 }
777
778                 at_rec = backsql_ad2at( oc, at->a_desc ); 
779   
780                 if ( at_rec == NULL ) {
781                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
782                                 "attribute \"%s\" is not registered "
783                                 "in objectclass \"%s\"\n",
784                                 at->a_desc->ad_cname.bv_val,
785                                 BACKSQL_OC_NAME( oc ), 0 );
786
787                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
788                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
789                                 rs->sr_text = "operation not permitted "
790                                         "within namingContext";
791                                 goto done;
792                         }
793
794                         continue;
795                 }
796                 
797                 if ( at_rec->bam_add_proc == NULL ) {
798                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
799                                 "add procedure is not defined "
800                                 "for attribute \"%s\"\n",
801                                 at->a_desc->ad_cname.bv_val, 0, 0 );
802
803                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
804                                 rs->sr_err = LDAP_UNWILLING_TO_PERFORM;
805                                 rs->sr_text = "operation not permitted "
806                                         "within namingContext";
807                                 goto done;
808                         }
809
810                         continue;
811                 }
812
813 #ifdef BACKSQL_REALLOC_STMT
814                 rc = backsql_Prepare( dbh, &sth, at_rec->bam_add_proc, 0 );
815                 if ( rc != SQL_SUCCESS ) {
816
817                         if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
818                                 rs->sr_err = LDAP_OTHER;
819                                 rs->sr_text = "SQL-backend error";
820                                 goto done;
821                         }
822
823                         continue;
824                 }
825 #endif /* BACKSQL_REALLOC_STMT */
826
827                 if ( BACKSQL_IS_ADD( at_rec->bam_expect_return ) ) {
828                         pno = 1;
829                         SQLBindParameter( sth, 1, SQL_PARAM_OUTPUT,
830                                         SQL_C_ULONG, SQL_INTEGER,
831                                         0, 0, &prc, 0, 0 );
832                 } else {
833                         pno = 0;
834                 }
835
836                 po = ( BACKSQL_IS_ADD( at_rec->bam_param_order ) ) > 0;
837                 currpos = pno + 1 + po;
838                 SQLBindParameter( sth, currpos,
839                                 SQL_PARAM_INPUT, SQL_C_ULONG,
840                                 SQL_INTEGER, 0, 0, &new_keyval, 0, 0 );
841                 currpos = pno + 2 - po;
842
843                 for ( i = 0, at_val = &at->a_vals[ i ];
844                                 at_val->bv_val != NULL;
845                                 i++, at_val = &at->a_vals[ i ] ) {
846
847                         /*
848                          * Do not deal with the objectClass that is used
849                          * to build the entry
850                          */
851                         if ( at->a_desc == slap_schema.si_ad_objectClass ) {
852                                 if ( bvmatch( at_val, &oc->bom_oc->soc_cname ) ) {
853                                         continue;
854                                 }
855                         }
856
857                         /*
858                          * check for syntax needed here 
859                          * maybe need binary bind?
860                          */
861
862                         backsql_BindParamStr( sth, currpos,
863                                         at_val->bv_val, at_val->bv_len + 1 );
864 #ifdef SECURITY_PARANOID
865                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
866                                 "executing \"%s\", id=%ld\n", 
867                                 at_rec->bam_add_proc, new_keyval, 0 );
868 #else
869                         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
870                                 "executing \"%s\" for val[%d], id=%ld\n", 
871                                 at_rec->bam_add_proc, i, new_keyval );
872 #endif
873 #ifndef BACKSQL_REALLOC_STMT
874                         rc = SQLExecDirect( sth, at_rec->bam_add_proc, SQL_NTS );
875 #else /* BACKSQL_REALLOC_STMT */
876                         rc = SQLExecute( sth );
877 #endif /* BACKSQL_REALLOC_STMT */
878                         if ( rc != SQL_SUCCESS ) {
879                                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
880                                         "add_proc execution failed\n", 
881                                         0, 0, 0 );
882                                 backsql_PrintErrors( bi->db_env, dbh, sth, rc );
883
884                                 if ( BACKSQL_FAIL_IF_NO_MAPPING( bi ) ) {
885                                         rs->sr_err = LDAP_OTHER;
886                                         rs->sr_text = "SQL-backend error";
887                                         goto done;
888                                 }
889                         }
890                 }
891 #ifndef BACKSQL_REALLOC_STMT
892                 SQLFreeStmt( sth, SQL_RESET_PARAMS ); 
893 #else /* BACKSQL_REALLOC_STMT */
894                 SQLFreeStmt( sth, SQL_DROP );
895 #endif /* BACKSQL_REALLOC_STMT */
896         }
897
898 #ifdef BACKSQL_REALLOC_STMT
899         rc = backsql_Prepare( dbh, &sth, bi->insentry_query, 0 );
900         if ( rc != SQL_SUCCESS ) {
901                 rs->sr_err = LDAP_OTHER;
902                 rs->sr_text = "SQL-backend error";
903                 goto done;
904         }
905 #endif /* BACKSQL_REALLOC_STMT */
906         
907         backsql_BindParamStr( sth, 1, op->oq_add.rs_e->e_name.bv_val,
908                         BACKSQL_MAX_DN_LEN );
909         SQLBindParameter( sth, 2, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
910                         0, 0, &oc->bom_id, 0, 0 );
911         SQLBindParameter( sth, 3, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
912                         0, 0, &parent_id.id, 0, 0 );
913         SQLBindParameter( sth, 4, SQL_PARAM_INPUT, SQL_C_LONG, SQL_INTEGER,
914                         0, 0, &new_keyval, 0, 0 );
915
916         Debug( LDAP_DEBUG_TRACE, "   backsql_add(): executing \"%s\" for dn \"%s\"\n",
917                         bi->insentry_query, op->oq_add.rs_e->e_name.bv_val, 0 );
918         Debug( LDAP_DEBUG_TRACE, "                  for oc_map_id=%ld, parent_id=%ld, "
919                         "keyval=%ld\n", oc->bom_id, parent_id.id, new_keyval );
920 #ifndef BACKSQL_REALLOC_STMT
921         rc = SQLExecDirect( sth, bi->insentry_query, SQL_NTS );
922 #else /* BACKSQL_REALLOC_STMT */
923         rc = SQLExecute( sth );
924 #endif /* BACKSQL_REALLOC_STMT */
925         if ( rc != SQL_SUCCESS ) {
926                 Debug( LDAP_DEBUG_TRACE, "   backsql_add(): "
927                         "could not insert ldap_entries record\n", 0, 0, 0 );
928                 backsql_PrintErrors( bi->db_env, dbh, sth, rc );
929                 
930                 /*
931                  * execute delete_proc to delete data added !!!
932                  */
933                 SQLFreeStmt( sth, SQL_DROP );
934                 rs->sr_err = LDAP_OTHER;
935                 rs->sr_text = "SQL-backend error";
936                 goto done;
937         }
938         
939         SQLFreeStmt( sth, SQL_DROP );
940
941         /*
942          * Commit only if all operations succeed
943          */
944         SQLTransact( SQL_NULL_HENV, dbh, 
945                         op->o_noop ? SQL_ROLLBACK : SQL_COMMIT );
946
947         /*
948          * FIXME: NOOP does not work for add -- it works for all 
949          * the other operations, and I don't get the reason :(
950          * 
951          * hint: there might be some autocommit in Postgres
952          * so that when the unique id of the key table is
953          * automatically increased, there's no rollback.
954          * We might implement a "rollback" procedure consisting
955          * in deleting that row.
956          */
957
958 done:;
959         send_ldap_result( op, rs );
960
961         Debug( LDAP_DEBUG_TRACE, "<==backsql_add(): %d%s%s\n",
962                         rs->sr_err,
963                         rs->sr_text ? ": " : "",
964                         rs->sr_text ? rs->sr_text : "" );
965
966         return ( ( rs->sr_err == LDAP_SUCCESS ) ? op->o_noop : 1 );
967 }
968
969 #endif /* SLAPD_SQL */
970