]> git.sur5r.net Git - openldap/blob - servers/slapd/back-mdb/attr.c
More for indexing, drop dbcache
[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         }
113
114         flags = MDB_DUPSORT|MDB_DUPFIXED|MDB_INTEGERDUP;
115         if ( !(slapMode & SLAP_TOOL_READONLY) )
116                 flags |= MDB_CREATE;
117
118         for ( i=0; i<mdb->mi_nattrs; i++ ) {
119                 if ( mdb->mi_attrs[i]->ai_dbi ) /* already open */
120                         continue;
121                 rc = mdb_open( txn, mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
122                         flags, &mdb->mi_attrs[i]->ai_dbi );
123                 if ( rc ) {
124                         snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
125                                 "mdb_open(%s) failed: %s (%d).",
126                                 be->be_suffix[0].bv_val,
127                                 mdb->mi_attrs[i]->ai_desc->ad_type->sat_cname.bv_val,
128                                 mdb_strerror(rc), rc );
129                         Debug( LDAP_DEBUG_ANY,
130                                 LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
131                                 cr->msg, 0, 0 );
132                         break;
133                 }
134         }
135
136         /* Only commit if this is our txn */
137         if ( tx0 == NULL ) {
138                 if ( !rc ) {
139                         rc = mdb_txn_commit( txn );
140                         if ( rc ) {
141                                 snprintf( cr->msg, sizeof(cr->msg), "database \"%s\": "
142                                         "txn_commit failed: %s (%d).",
143                                         be->be_suffix[0].bv_val, mdb_strerror(rc), rc );
144                                 Debug( LDAP_DEBUG_ANY,
145                                         LDAP_XSTRING(mdb_attr_dbs) ": %s\n",
146                                         cr->msg, 0, 0 );
147                         }
148                 } else {
149                         mdb_txn_abort( txn );
150                 }
151         }
152
153         return rc;
154 }
155
156 void
157 mdb_attr_dbs_close(
158         struct mdb_info *mdb,
159         MDB_txn *txn
160 )
161 {
162         int i;
163         for ( i=0; i<mdb->mi_nattrs; i++ )
164                 if ( mdb->mi_attrs[i]->ai_dbi )
165                         mdb_close( txn, mdb->mi_attrs[i]->ai_dbi );
166 }
167
168 int
169 mdb_attr_index_config(
170         struct mdb_info *mdb,
171         const char              *fname,
172         int                     lineno,
173         int                     argc,
174         char            **argv,
175         struct          config_reply_s *c_reply)
176 {
177         int rc = 0;
178         int     i;
179         slap_mask_t mask;
180         char **attrs;
181         char **indexes = NULL;
182
183         attrs = ldap_str2charray( argv[0], "," );
184
185         if( attrs == NULL ) {
186                 fprintf( stderr, "%s: line %d: "
187                         "no attributes specified: %s\n",
188                         fname, lineno, argv[0] );
189                 return LDAP_PARAM_ERROR;
190         }
191
192         if ( argc > 1 ) {
193                 indexes = ldap_str2charray( argv[1], "," );
194
195                 if( indexes == NULL ) {
196                         fprintf( stderr, "%s: line %d: "
197                                 "no indexes specified: %s\n",
198                                 fname, lineno, argv[1] );
199                         rc = LDAP_PARAM_ERROR;
200                         goto done;
201                 }
202         }
203
204         if( indexes == NULL ) {
205                 mask = mdb->mi_defaultmask;
206
207         } else {
208                 mask = 0;
209
210                 for ( i = 0; indexes[i] != NULL; i++ ) {
211                         slap_mask_t index;
212                         rc = slap_str2index( indexes[i], &index );
213
214                         if( rc != LDAP_SUCCESS ) {
215                                 if ( c_reply )
216                                 {
217                                         snprintf(c_reply->msg, sizeof(c_reply->msg),
218                                                 "index type \"%s\" undefined", indexes[i] );
219
220                                         fprintf( stderr, "%s: line %d: %s\n",
221                                                 fname, lineno, c_reply->msg );
222                                 }
223                                 rc = LDAP_PARAM_ERROR;
224                                 goto done;
225                         }
226
227                         mask |= index;
228                 }
229         }
230
231         if( !mask ) {
232                 if ( c_reply )
233                 {
234                         snprintf(c_reply->msg, sizeof(c_reply->msg),
235                                 "no indexes selected" );
236                         fprintf( stderr, "%s: line %d: %s\n",
237                                 fname, lineno, c_reply->msg );
238                 }
239                 rc = LDAP_PARAM_ERROR;
240                 goto done;
241         }
242
243         for ( i = 0; attrs[i] != NULL; i++ ) {
244                 AttrInfo        *a;
245                 AttributeDescription *ad;
246                 const char *text;
247 #ifdef LDAP_COMP_MATCH
248                 ComponentReference* cr = NULL;
249                 AttrInfo *a_cr = NULL;
250 #endif
251
252                 if( strcasecmp( attrs[i], "default" ) == 0 ) {
253                         mdb->mi_defaultmask |= mask;
254                         continue;
255                 }
256
257 #ifdef LDAP_COMP_MATCH
258                 if ( is_component_reference( attrs[i] ) ) {
259                         rc = extract_component_reference( attrs[i], &cr );
260                         if ( rc != LDAP_SUCCESS ) {
261                                 if ( c_reply )
262                                 {
263                                         snprintf(c_reply->msg, sizeof(c_reply->msg),
264                                                 "index component reference\"%s\" undefined",
265                                                 attrs[i] );
266                                         fprintf( stderr, "%s: line %d: %s\n",
267                                                 fname, lineno, c_reply->msg );
268                                 }
269                                 goto done;
270                         }
271                         cr->cr_indexmask = mask;
272                         /*
273                          * After extracting a component reference
274                          * only the name of a attribute will be remaining
275                          */
276                 } else {
277                         cr = NULL;
278                 }
279 #endif
280                 ad = NULL;
281                 rc = slap_str2ad( attrs[i], &ad, &text );
282
283                 if( rc != LDAP_SUCCESS ) {
284                         if ( c_reply )
285                         {
286                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
287                                         "index attribute \"%s\" undefined",
288                                         attrs[i] );
289
290                                 fprintf( stderr, "%s: line %d: %s\n",
291                                         fname, lineno, c_reply->msg );
292                         }
293                         goto done;
294                 }
295
296                 if( ad == slap_schema.si_ad_entryDN || slap_ad_is_binary( ad ) ) {
297                         if (c_reply) {
298                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
299                                         "index of attribute \"%s\" disallowed", attrs[i] );
300                                 fprintf( stderr, "%s: line %d: %s\n",
301                                         fname, lineno, c_reply->msg );
302                         }
303                         rc = LDAP_UNWILLING_TO_PERFORM;
304                         goto done;
305                 }
306
307                 if( IS_SLAP_INDEX( mask, SLAP_INDEX_APPROX ) && !(
308                         ad->ad_type->sat_approx
309                                 && ad->ad_type->sat_approx->smr_indexer
310                                 && ad->ad_type->sat_approx->smr_filter ) )
311                 {
312                         if (c_reply) {
313                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
314                                         "approx index of attribute \"%s\" disallowed", attrs[i] );
315                                 fprintf( stderr, "%s: line %d: %s\n",
316                                         fname, lineno, c_reply->msg );
317                         }
318                         rc = LDAP_INAPPROPRIATE_MATCHING;
319                         goto done;
320                 }
321
322                 if( IS_SLAP_INDEX( mask, SLAP_INDEX_EQUALITY ) && !(
323                         ad->ad_type->sat_equality
324                                 && ad->ad_type->sat_equality->smr_indexer
325                                 && ad->ad_type->sat_equality->smr_filter ) )
326                 {
327                         if (c_reply) {
328                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
329                                         "equality index of attribute \"%s\" disallowed", attrs[i] );
330                                 fprintf( stderr, "%s: line %d: %s\n",
331                                         fname, lineno, c_reply->msg );
332                         }
333                         rc = LDAP_INAPPROPRIATE_MATCHING;
334                         goto done;
335                 }
336
337                 if( IS_SLAP_INDEX( mask, SLAP_INDEX_SUBSTR ) && !(
338                         ad->ad_type->sat_substr
339                                 && ad->ad_type->sat_substr->smr_indexer
340                                 && ad->ad_type->sat_substr->smr_filter ) )
341                 {
342                         if (c_reply) {
343                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
344                                         "substr index of attribute \"%s\" disallowed", attrs[i] );
345                                 fprintf( stderr, "%s: line %d: %s\n",
346                                         fname, lineno, c_reply->msg );
347                         }
348                         rc = LDAP_INAPPROPRIATE_MATCHING;
349                         goto done;
350                 }
351
352                 Debug( LDAP_DEBUG_CONFIG, "index %s 0x%04lx\n",
353                         ad->ad_cname.bv_val, mask, 0 ); 
354
355                 a = (AttrInfo *) ch_malloc( sizeof(AttrInfo) );
356
357 #ifdef LDAP_COMP_MATCH
358                 a->ai_cr = NULL;
359 #endif
360                 a->ai_desc = ad;
361
362                 if ( mdb->mi_flags & MDB_IS_OPEN ) {
363                         a->ai_indexmask = 0;
364                         a->ai_newmask = mask;
365                 } else {
366                         a->ai_indexmask = mask;
367                         a->ai_newmask = 0;
368                 }
369
370 #ifdef LDAP_COMP_MATCH
371                 if ( cr ) {
372                         a_cr = mdb_attr_mask( mdb, ad );
373                         if ( a_cr ) {
374                                 /*
375                                  * AttrInfo is already in AVL
376                                  * just add the extracted component reference
377                                  * in the AttrInfo
378                                  */
379                                 rc = insert_component_reference( cr, &a_cr->ai_cr );
380                                 if ( rc != LDAP_SUCCESS) {
381                                         fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
382                                         rc = LDAP_PARAM_ERROR;
383                                         goto done;
384                                 }
385                                 continue;
386                         } else {
387                                 rc = insert_component_reference( cr, &a->ai_cr );
388                                 if ( rc != LDAP_SUCCESS) {
389                                         fprintf( stderr, " error during inserting component reference in %s ", attrs[i]);
390                                         rc = LDAP_PARAM_ERROR;
391                                         goto done;
392                                 }
393                         }
394                 }
395 #endif
396                 rc = ainfo_insert( mdb, a );
397                 if( rc ) {
398                         if ( mdb->mi_flags & MDB_IS_OPEN ) {
399                                 AttrInfo *b = mdb_attr_mask( mdb, ad );
400                                 /* If there is already an index defined for this attribute
401                                  * it must be replaced. Otherwise we end up with multiple 
402                                  * olcIndex values for the same attribute */
403                                 if ( b->ai_indexmask & MDB_INDEX_DELETING ) {
404                                         /* If we were editing this attr, reset it */
405                                         b->ai_indexmask &= ~MDB_INDEX_DELETING;
406                                         /* If this is leftover from a previous add, commit it */
407                                         if ( b->ai_newmask )
408                                                 b->ai_indexmask = b->ai_newmask;
409                                         b->ai_newmask = a->ai_newmask;
410                                         ch_free( a );
411                                         rc = 0;
412                                         continue;
413                                 }
414                         }
415                         if (c_reply) {
416                                 snprintf(c_reply->msg, sizeof(c_reply->msg),
417                                         "duplicate index definition for attr \"%s\"",
418                                         attrs[i] );
419                                 fprintf( stderr, "%s: line %d: %s\n",
420                                         fname, lineno, c_reply->msg );
421                         }
422
423                         rc = LDAP_PARAM_ERROR;
424                         goto done;
425                 }
426         }
427
428 done:
429         ldap_charray_free( attrs );
430         if ( indexes != NULL ) ldap_charray_free( indexes );
431
432         return rc;
433 }
434
435 static int
436 mdb_attr_index_unparser( void *v1, void *v2 )
437 {
438         AttrInfo *ai = v1;
439         BerVarray *bva = v2;
440         struct berval bv;
441         char *ptr;
442
443         slap_index2bvlen( ai->ai_indexmask, &bv );
444         if ( bv.bv_len ) {
445                 bv.bv_len += ai->ai_desc->ad_cname.bv_len + 1;
446                 ptr = ch_malloc( bv.bv_len+1 );
447                 bv.bv_val = lutil_strcopy( ptr, ai->ai_desc->ad_cname.bv_val );
448                 *bv.bv_val++ = ' ';
449                 slap_index2bv( ai->ai_indexmask, &bv );
450                 bv.bv_val = ptr;
451                 ber_bvarray_add( bva, &bv );
452         }
453         return 0;
454 }
455
456 static AttributeDescription addef = { NULL, NULL, BER_BVC("default") };
457 static AttrInfo aidef = { &addef };
458
459 void
460 mdb_attr_index_unparse( struct mdb_info *mdb, BerVarray *bva )
461 {
462         int i;
463
464         if ( mdb->mi_defaultmask ) {
465                 aidef.ai_indexmask = mdb->mi_defaultmask;
466                 mdb_attr_index_unparser( &aidef, bva );
467         }
468         for ( i=0; i<mdb->mi_nattrs; i++ )
469                 mdb_attr_index_unparser( mdb->mi_attrs[i], bva );
470 }
471
472 void
473 mdb_attr_info_free( AttrInfo *ai )
474 {
475 #ifdef LDAP_COMP_MATCH
476         free( ai->ai_cr );
477 #endif
478         free( ai );
479 }
480
481 void
482 mdb_attr_index_destroy( struct mdb_info *mdb )
483 {
484         int i;
485
486         for ( i=0; i<mdb->mi_nattrs; i++ ) 
487                 mdb_attr_info_free( mdb->mi_attrs[i] );
488
489         free( mdb->mi_attrs );
490 }
491
492 void mdb_attr_index_free( struct mdb_info *mdb, AttributeDescription *ad )
493 {
494         int i;
495
496         i = mdb_attr_slot( mdb, ad, NULL );
497         if ( i >= 0 ) {
498                 mdb_attr_info_free( mdb->mi_attrs[i] );
499                 mdb->mi_nattrs--;
500                 for (; i<mdb->mi_nattrs; i++)
501                         mdb->mi_attrs[i] = mdb->mi_attrs[i+1];
502         }
503 }
504
505 void mdb_attr_flush( struct mdb_info *mdb )
506 {
507         int i;
508
509         for ( i=0; i<mdb->mi_nattrs; i++ ) {
510                 if ( mdb->mi_attrs[i]->ai_indexmask & MDB_INDEX_DELETING ) {
511                         int j;
512                         mdb_attr_info_free( mdb->mi_attrs[i] );
513                         mdb->mi_nattrs--;
514                         for (j=i; j<mdb->mi_nattrs; j++)
515                                 mdb->mi_attrs[j] = mdb->mi_attrs[j+1];
516                         i--;
517                 }
518         }
519 }