]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/attr.c
Tweak back-mdb integer types to match libmdb.
[openldap] / servers / slapd / back-mdb / attr.c
1 /* attr.c - backend routines for dealing with attributes */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2011 The OpenLDAP Foundation.
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
17 #include "portable.h"
18
19 #include <stdio.h>
20
21 #include <ac/socket.h>
22 #include <ac/string.h>
23
24 #include "slap.h"
25 #include "back-mdb.h"
26 #include "config.h"
27 #include "lutil.h"
28
29 /* Find the ad, return -1 if not found,
30  * set point for insertion if ins is non-NULL
31  */
32 int
33 mdb_attr_slot( struct mdb_info *mdb, AttributeDescription *ad, int *ins )
34 {
35         unsigned base = 0, cursor = 0;
36         unsigned n = mdb->mi_nattrs;
37         int val = 0;
38         
39         while ( 0 < n ) {
40                 unsigned pivot = n >> 1;
41                 cursor = base + pivot;
42
43                 val = SLAP_PTRCMP( ad, mdb->mi_attrs[cursor]->ai_desc );
44                 if ( val < 0 ) {
45                         n = pivot;
46                 } else if ( val > 0 ) {
47                         base = cursor + 1;
48                         n -= pivot + 1;
49                 } else {
50                         return cursor;
51                 }
52         }
53         if ( ins ) {
54                 if ( val > 0 )
55                         ++cursor;
56                 *ins = cursor;
57         }
58         return -1;
59 }
60
61 static int
62 ainfo_insert( struct mdb_info *mdb, AttrInfo *a )
63 {
64         int x;
65         int i = mdb_attr_slot( mdb, a->ai_desc, &x );
66
67         /* Is it a dup? */
68         if ( i >= 0 )
69                 return -1;
70
71         mdb->mi_attrs = ch_realloc( mdb->mi_attrs, ( mdb->mi_nattrs+1 ) * 
72                 sizeof( AttrInfo * ));
73         if ( x < mdb->mi_nattrs )
74                 AC_MEMCPY( &mdb->mi_attrs[x+1], &mdb->mi_attrs[x],
75                         ( mdb->mi_nattrs - x ) * sizeof( AttrInfo *));
76         mdb->mi_attrs[x] = a;
77         mdb->mi_nattrs++;
78         return 0;
79 }
80
81 AttrInfo *
82 mdb_attr_mask(
83         struct mdb_info *mdb,
84         AttributeDescription *desc )
85 {
86         int i = mdb_attr_slot( mdb, desc, NULL );
87         return i < 0 ? NULL : mdb->mi_attrs[i];
88 }
89
90 /* Open all un-opened index DB handles */
91 int
92 mdb_attr_dbs_open(
93         BackendDB *be, MDB_txn *tx0, ConfigReply *cr )
94 {
95         struct mdb_info *mdb = (struct mdb_info *) be->be_private;
96         MDB_txn *txn;
97         int i, flags;
98         int rc;
99
100         txn = tx0;
101         if ( txn == NULL ) {
102                 rc = mdb_txn_begin( mdb->mi_dbenv, 0, &txn );
103                 if ( rc ) {
104                         snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
105                                 "txn_begin failed: %s (%d).",
106                                 be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
107                         Debug( LDAP_DEBUG_ANY,
108                                 LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
109                                 cr->msg, 0, 0 );
110                         return rc;
111                 }
112         } else {
113                 rc = 0;
114         }
115
116         flags = MDB_DUPSORT|MDB_DUPFIXED|MDB_INTEGERDUP;
117         if ( !(slapMode & SLAP_TOOL_READONLY) )
118                 flags |= MDB_CREATE;
119
120         for ( i=0; i<mdb->mi_nattrs; i++ ) {
121                 if ( mdb->mi_attrs[i]->ai_dbi ) /* already open */
122                         continue;
123                 rc = mdb_open( txn, mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
124                         flags, &mdb->mi_attrs[i]->ai_dbi );
125                 if ( rc ) {
126                         snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
127                                 "mdb_open(%s) failed: %s (%d).",
128                                 be->be_suffix[0].bv_val,
129                                 mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
130                                 mdb_strerror(rc), rc );
131                         Debug( LDAP_DEBUG_ANY,
132                                 LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
133                                 cr->msg, 0, 0 );
134                         break;
135                 }
136         }
137
138         /* Only commit if this is our txn */
139         if ( tx0 == NULL ) {
140                 if ( !rc ) {
141                         rc = mdb_txn_commit( txn );
142                         if ( rc ) {
143                                 snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
144                                         "txn_commit failed: %s (%d).",
145                                         be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
146                                 Debug( LDAP_DEBUG_ANY,
147                                         LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
148                                         cr->msg, 0, 0 );
149                         }
150                 } else {
151                         mdb_txn_abort( txn );
152                 }
153         }
154
155         return rc;
156 }
157
158 void
159 mdb_attr_dbs_close(
160         struct mdb_info *mdb,
161         MDB_txn *txn
162 )
163 {
164         int i;
165         for ( i=0; i<mdb->mi_nattrs; i++ )
166                 if ( mdb->mi_attrs[i]->ai_dbi )
167                         mdb_close( txn, mdb->mi_attrs[i]->ai_dbi );
168 }
169
170 int
171 mdb_attr_index_config(
172         struct mdb_info *mdb,
173         const char              *fname,
174         int                     lineno,
175         int                     argc,
176         char            **argv,
177         struct          config_reply_s *c_reply)
178 {
179         int rc = 0;
180         int     i;
181         slap_mask_t mask;
182         char **attrs;
183         char **indexes = NULL;
184
185         attrs = ldap_str2charray( argv[0], "," );
186
187         if( attrs == NULL ) {
188                 fprintf( stderr, "%s: line %d: "
189                         "no attributes specified: %s\n",
190                         fname, lineno, argv[0] );
191                 return LDAP_PARAM_ERROR;
192         }
193
194         if ( argc > 1 ) {
195                 indexes = ldap_str2charray( argv[1], "," );
196
197                 if( indexes == NULL ) {
198                         fprintf( stderr, "%s: line %d: "
199                                 "no indexes specified: %s\n",
200                                 fname, lineno, argv[1] );
201                         rc = LDAP_PARAM_ERROR;
202                         goto done;
203                 }
204         }
205
206         if( indexes == NULL ) {
207                 mask = mdb->mi_defaultmask;
208
209         } else {
210                 mask = 0;
211
212                 for ( i = 0; indexes[i] != NULL; i++ ) {
213                         slap_mask_t index;
214                         rc = slap_str2index( indexes[i], &index );
215
216                         if( rc != LDAP_SUCCESS ) {
217                                 if ( c_reply )
218                                 {
219                                         snprintf(c_reply->msg, sizeof(c_reply->msg),
220                                                 "index type \"%s\" undefined", indexes[i] );
221
222                                         fprintf( stderr, "%s: line %d: %s\n",
223                                                 fname, lineno, c_reply->msg );
224                                 }
225                                 rc = LDAP_PARAM_ERROR;
226                                 goto done;
227                         }
228
229                         mask |= index;
230                 }
231         }
232
233         if( !mask ) {
234                 if ( c_reply )
235                 {
236                         snprintf(c_reply->msg, sizeof(c_reply->msg),
237                                 "no indexes selected" );
238                         fprintf( stderr, "%s: line %d: %s\n",
239                                 fname, lineno, c_reply->msg );
240                 }
241                 rc = LDAP_PARAM_ERROR;
242                 goto done;
243         }
244
245         for ( i = 0; attrs[i] != NULL; i++ ) {
246                 AttrInfo        *a;
247                 AttributeDescription *ad;
248                 const char *text;
249 #ifdef LDAP_COMP_MATCH
250                 ComponentReference* cr = NULL;
251                 AttrInfo *a_cr = NULL;
252 #endif
253
254                 if( strcasecmp( attrs[i], "default" ) == 0 ) {
255                         mdb->mi_defaultmask |= mask;
256                         continue;
257                 }
258
259 #ifdef LDAP_COMP_MATCH
260                 if ( is_component_reference( attrs[i] ) ) {
261                         rc = extract_component_reference( attrs[i], &cr );
262                         if ( rc != LDAP_SUCCESS ) {
263                                 if ( c_reply )
264                                 {
265                                         snprintf(c_reply->msg, sizeof(c_reply->msg),
266                                                 "index component reference\"%s\" undefined",
267                                                 attrs[i] );
268                                         fprintf( stderr, "%s: line %d: %s\n",
269                                                 fname, lineno, c_reply->msg );
270                                 }
271                                 goto done;
272                         }
273                         cr->cr_indexmask = mask;
274                         /*
275                          * After extracting a component reference
276                          * only the name of a attribute will be remaining
277                          */
278                 } else {
279                         cr = NULL;
280                 }
281 #endif
282                 ad = NULL;
283                 rc = slap_str2ad( attrs[i], &ad, &text );
284
285                 if( rc != LDAP_SUCCESS ) {
286                         if ( c_reply )
287                         {
288                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
289                                         "index attribute \"%s\" undefined",
290                                         attrs[i] );
291
292                                 fprintf( stderr, "%s: line %d: %s\n",
293                                         fname, lineno, c_reply->msg );
294                         }
295                         goto done;
296                 }
297
298                 if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) {
299                         if (c_reply) {
300                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
301                                         "index of attribute \"%s\" disallowed", attrs[i] );
302                                 fprintf( stderr, "%s: line %d: %s\n",
303                                         fname, lineno, c_reply->msg );
304                         }
305                         rc = LDAP_UNWILLING_TO_PERFORM;
306                         goto done;
307                 }
308
309                 if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !(
310                         ad->ad_type->sat_approx
311                                 && ad->ad_type->sat_approx->smr_indexer
312                                 && ad->ad_type->sat_approx->smr_filter ) )
313                 {
314                         if (c_reply) {
315                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
316                                         "approx index of attribute \"%s\" disallowed", attrs[i] );
317                                 fprintf( stderr, "%s: line %d: %s\n",
318                                         fname, lineno, c_reply->msg );
319                         }
320                         rc = LDAP_INAPPROPRIATE_MATCHING;
321                         goto done;
322                 }
323
324                 if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !(
325                         ad->ad_type->sat_equality
326                                 && ad->ad_type->sat_equality->smr_indexer
327                                 && ad->ad_type->sat_equality->smr_filter ) )
328                 {
329                         if (c_reply) {
330                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
331                                         "equality index of attribute \"%s\" disallowed", attrs[i] );
332                                 fprintf( stderr, "%s: line %d: %s\n",
333                                         fname, lineno, c_reply->msg );
334                         }
335                         rc = LDAP_INAPPROPRIATE_MATCHING;
336                         goto done;
337                 }
338
339                 if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !(
340                         ad->ad_type->sat_substr
341                                 && ad->ad_type->sat_substr->smr_indexer
342                                 && ad->ad_type->sat_substr->smr_filter ) )
343                 {
344                         if (c_reply) {
345                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
346                                         "substr index of attribute \"%s\" disallowed", attrs[i] );
347                                 fprintf( stderr, "%s: line %d: %s\n",
348                                         fname, lineno, c_reply->msg );
349                         }
350                         rc = LDAP_INAPPROPRIATE_MATCHING;
351                         goto done;
352                 }
353
354                 Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
355                         ad->ad_cname.bv_val, mask, 0 ); 
356
357                 a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
358
359 #ifdef LDAP_COMP_MATCH
360                 a->ai_cr = NULL;
361 #endif
362                 a->ai_desc = ad;
363                 a->ai_dbi = 0;
364
365                 if ( mdb->mi_flags & MDB_IS_OPEN ) {
366                         a->ai_indexmask = 0;
367                         a->ai_newmask = mask;
368                 } else {
369                         a->ai_indexmask = mask;
370                         a->ai_newmask = 0;
371                 }
372
373 #ifdef LDAP_COMP_MATCH
374                 if ( cr ) {
375                         a_cr = mdb_attr_mask( mdb, ad );
376                         if ( a_cr ) {
377                                 /*
378                                  * AttrInfo is already in AVL
379                                  * just add the extracted component reference
380                                  * in the AttrInfo
381                                  */
382                                 rc = insert_component_reference( cr, &a_cr->ai_cr );
383                                 if ( rc != LDAP_SUCCESS) {
384                                         fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
385                                         rc = LDAP_PARAM_ERROR;
386                                         goto done;
387                                 }
388                                 continue;
389                         } else {
390                                 rc = insert_component_reference( cr, &a->ai_cr );
391                                 if ( rc != LDAP_SUCCESS) {
392                                         fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
393                                         rc = LDAP_PARAM_ERROR;
394                                         goto done;
395                                 }
396                         }
397                 }
398 #endif
399                 rc = ainfo_insert( mdb, a );
400                 if( rc ) {
401                         if ( mdb->mi_flags & MDB_IS_OPEN ) {
402                                 AttrInfo *b = mdb_attr_mask( mdb, ad );
403                                 /* If there is already an index defined for this attribute
404                                  * it must be replaced. Otherwise we end up with multiple 
405                                  * olcIndex values for the same attribute */
406                                 if ( b->ai_indexmask & MDB_INDEX_DELETING ) {
407                                         /* If we were editing this attr, reset it */
408                                         b->ai_indexmask &= ~MDB_INDEX_DELETING;
409                                         /* If this is leftover from a previous add, commit it */
410                                         if ( b->ai_newmask )
411                                                 b->ai_indexmask = b->ai_newmask;
412                                         b->ai_newmask = a->ai_newmask;
413                                         ch_free( a );
414                                         rc = 0;
415                                         continue;
416                                 }
417                         }
418                         if (c_reply) {
419                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
420                                         "duplicate index definition for attr \"%s\"",
421                                         attrs[i] );
422                                 fprintf( stderr, "%s: line %d: %s\n",
423                                         fname, lineno, c_reply->msg );
424                         }
425
426                         rc = LDAP_PARAM_ERROR;
427                         goto done;
428                 }
429         }
430
431 done:
432         ldap_charray_free( attrs );
433         if ( indexes != NULL ) ldap_charray_free( indexes );
434
435         return rc;
436 }
437
438 static int
439 mdb_attr_index_unparser( void *v1, void *v2 )
440 {
441         AttrInfo *ai = v1;
442         BerVarray *bva = v2;
443         struct berval bv;
444         char *ptr;
445
446         slap_index2bvlen( ai->ai_indexmask, &bv );
447         if ( bv.bv_len ) {
448                 bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
449                 ptr = ch_malloc( bv.bv_len+1 );
450                 bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val );
451                 *bv.bv_val++ = ' ';
452                 slap_index2bv( ai->ai_indexmask, &bv );
453                 bv.bv_val = ptr;
454                 ber_bvarray_add( bva, &bv );
455         }
456         return 0;
457 }
458
459 static AttributeDescription addef = { NULL, NULL, BER_BVC("default") };
460 static AttrInfo aidef = { &addef };
461
462 void
463 mdb_attr_index_unparse( struct mdb_info *mdb, BerVarray *bva )
464 {
465         int i;
466
467         if ( mdb->mi_defaultmask ) {
468                 aidef.ai_indexmask = mdb->mi_defaultmask;
469                 mdb_attr_index_unparser( &aidef, bva );
470         }
471         for ( i=0; i<mdb->mi_nattrs; i++ )
472                 mdb_attr_index_unparser( mdb->mi_attrs[i], bva );
473 }
474
475 void
476 mdb_attr_info_free( AttrInfo *ai )
477 {
478 #ifdef LDAP_COMP_MATCH
479         free( ai->ai_cr );
480 #endif
481         free( ai );
482 }
483
484 void
485 mdb_attr_index_destroy( struct mdb_info *mdb )
486 {
487         int i;
488
489         for ( i=0; i<mdb->mi_nattrs; i++ ) 
490                 mdb_attr_info_free( mdb->mi_attrs[i] );
491
492         free( mdb->mi_attrs );
493 }
494
495 void mdb_attr_index_free( struct mdb_info *mdb, AttributeDescription *ad )
496 {
497         int i;
498
499         i = mdb_attr_slot( mdb, ad, NULL );
500         if ( i >= 0 ) {
501                 mdb_attr_info_free( mdb->mi_attrs[i] );
502                 mdb->mi_nattrs--;
503                 for (; i<mdb->mi_nattrs; i++)
504                         mdb->mi_attrs[i] = mdb->mi_attrs[i+1];
505         }
506 }
507
508 void mdb_attr_flush( struct mdb_info *mdb )
509 {
510         int i;
511
512         for ( i=0; i<mdb->mi_nattrs; i++ ) {
513                 if ( mdb->mi_attrs[i]->ai_indexmask & MDB_INDEX_DELETING ) {
514                         int j;
515                         mdb_attr_info_free( mdb->mi_attrs[i] );
516                         mdb->mi_nattrs--;
517                         for (j=i; j<mdb->mi_nattrs; j++)
518                                 mdb->mi_attrs[j] = mdb->mi_attrs[j+1];
519                         i--;
520                 }
521         }
522 }
523
524 int mdb_ad_read( struct mdb_info *mdb, MDB_txn *txn )
525 {
526         int i, rc;
527         MDB_cursor *mc;
528         MDB_val key, data;
529         struct berval bdata;
530         const char *text;
531         AttributeDescription *ad;
532
533         rc = mdb_cursor_open( txn, mdb->mi_ad2id, &mc );
534         if ( rc ) {
535                 Debug( LDAP_DEBUG_ANY,
536                         "mdb_ad_read: cursor_open failed %s(%d)\n",
537                         mdb_strerror(rc), rc, 0);
538                 return rc;
539         }
540
541         /* our array is 1-based, an index of 0 means no data */
542         i = mdb->mi_numads+1;
543         key.mv_size = sizeof(int);
544         key.mv_data = &i;
545
546         rc = mdb_cursor_get( mc, &key, &data, MDB_SET );
547
548         while ( rc == MDB_SUCCESS ) {
549                 bdata.bv_len = data.mv_size;
550                 bdata.bv_val = data.mv_data;
551                 ad = NULL;
552                 rc = slap_bv2ad( &bdata, &ad, &text );
553                 if ( rc ) {
554                         rc = slap_bv2undef_ad( &bdata, &mdb->mi_ads[i], &text, 0 );
555                 } else {
556                         mdb->mi_adxs[ad->ad_index] = i;
557                         mdb->mi_ads[i] = ad;
558                 }
559                 i++;
560                 rc = mdb_cursor_get( mc, &key, &data, MDB_NEXT );
561         }
562         mdb->mi_numads = i-1;
563
564 done:
565         if ( rc == MDB_NOTFOUND )
566                 rc = 0;
567
568         mdb_cursor_close( mc );
569
570         return rc;
571 }
572
573 int mdb_ad_get( struct mdb_info *mdb, MDB_txn *txn, AttributeDescription *ad )
574 {
575         int i, rc;
576         MDB_val key, val;
577
578         rc = mdb_ad_read( mdb, txn );
579         if (rc)
580                 return rc;
581
582         if ( mdb->mi_adxs[ad->ad_index] )
583                 return 0;
584
585         i = mdb->mi_numads+1;
586         key.mv_size = sizeof(int);
587         key.mv_data = &i;
588         val.mv_size = ad->ad_cname.bv_len;
589         val.mv_data = ad->ad_cname.bv_val;
590
591         rc = mdb_put( txn, mdb->mi_ad2id, &key, &val, 0 );
592         if ( rc == MDB_SUCCESS ) {
593                 mdb->mi_adxs[ad->ad_index] = i;
594                 mdb->mi_ads[i] = ad;
595                 mdb->mi_numads++;
596         } else {
597                 Debug( LDAP_DEBUG_ANY,
598                         "mdb_ad_get: mdb_put failed %s(%d)\n",
599                         mdb_strerror(rc), rc, 0);
600         }
601
602         return rc;
603 }