]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb/tools.c
Eliminate long-lived read transaction, we can just set a cursor's locker
[openldap] / servers / slapd / back-bdb / tools.c
1 /* tools.c - tools for slap tools */
2 /* $OpenLDAP$ */
3 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
4  *
5  * Copyright 2000-2005 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 #include <ac/string.h>
21
22 #define AVL_INTERNAL
23 #include "back-bdb.h"
24
25 static DBC *cursor = NULL;
26 static DBT key, data;
27
28 typedef struct dn_id {
29         ID id;
30         struct berval dn;
31 } dn_id;
32
33 #define HOLE_SIZE       4096
34 static dn_id hbuf[HOLE_SIZE], *holes = hbuf;
35 static unsigned nhmax = HOLE_SIZE;
36 static unsigned nholes;
37
38 static Avlnode *index_attrs, index_dummy;
39
40 int bdb_tool_entry_open(
41         BackendDB *be, int mode )
42 {
43         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
44
45         /* initialize key and data thangs */
46         DBTzero( &key );
47         DBTzero( &data );
48         key.flags = DB_DBT_REALLOC;
49         data.flags = DB_DBT_REALLOC;
50
51         if (cursor == NULL) {
52                 int rc = bdb->bi_id2entry->bdi_db->cursor(
53                         bdb->bi_id2entry->bdi_db, NULL, &cursor,
54                         bdb->bi_db_opflags );
55                 if( rc != 0 ) {
56                         return -1;
57                 }
58         }
59
60         return 0;
61 }
62
63 int bdb_tool_entry_close(
64         BackendDB *be )
65 {
66         assert( be != NULL );
67
68         if( key.data ) {
69                 ch_free( key.data );
70                 key.data = NULL;
71         }
72         if( data.data ) {
73                 ch_free( data.data );
74                 data.data = NULL;
75         }
76
77         if( cursor ) {
78                 cursor->c_close( cursor );
79                 cursor = NULL;
80         }
81
82         if( nholes ) {
83                 unsigned i;
84                 fprintf( stderr, "Error, entries missing!\n");
85                 for (i=0; i<nholes; i++) {
86                         fprintf(stderr, "  entry %ld: %s\n",
87                                 holes[i].id, holes[i].dn.bv_val);
88                 }
89                 return -1;
90         }
91                         
92         return 0;
93 }
94
95 static int bdb_reindex_cmp(const void *a, const void *b) { return 0; }
96
97 ID bdb_tool_entry_next(
98         BackendDB *be )
99 {
100         int rc;
101         ID id;
102         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
103
104         assert( be != NULL );
105         assert( slapMode & SLAP_TOOL_MODE );
106         assert( bdb != NULL );
107         
108         rc = cursor->c_get( cursor, &key, &data, DB_NEXT );
109
110         if( rc != 0 ) {
111                 /* If we're doing linear indexing and there are more attrs to
112                  * index, and we're at the end of the database, start over.
113                  */
114                 if ( bdb->bi_attrs == &index_dummy ) {
115                         if ( index_attrs && rc == DB_NOTFOUND ) {
116                                 /* optional - do a checkpoint here? */
117                                 index_dummy.avl_data = avl_delete(&index_attrs, NULL, bdb_reindex_cmp);
118                                 rc = cursor->c_get( cursor, &key, &data, DB_FIRST );
119                         }
120                         if ( rc ) {
121                                 bdb->bi_attrs = NULL;
122                                 return NOID;
123                         }
124                 } else {
125                         return NOID;
126                 }
127         }
128
129         if( data.data == NULL ) {
130                 return NOID;
131         }
132
133         BDB_DISK2ID( key.data, &id );
134         return id;
135 }
136
137 ID bdb_tool_dn2id_get(
138         Backend *be,
139         struct berval *dn
140 )
141 {
142         Operation op = {0};
143         Opheader ohdr = {0};
144         EntryInfo *ei = NULL;
145         int rc;
146
147         if ( BER_BVISEMPTY(dn) )
148                 return 0;
149
150         op.o_hdr = &ohdr;
151         op.o_bd = be;
152         op.o_tmpmemctx = NULL;
153         op.o_tmpmfuncs = &ch_mfuncs;
154
155         rc = bdb_cache_find_ndn( &op, NULL, dn, &ei );
156         if ( ei ) bdb_cache_entryinfo_unlock( ei );
157         if ( rc == DB_NOTFOUND )
158                 return NOID;
159         
160         return ei->bei_id;
161 }
162
163 int bdb_tool_id2entry_get(
164         Backend *be,
165         ID id,
166         Entry **e
167 )
168 {
169         int rc = bdb_id2entry( be, NULL, 0, id, e );
170
171         if ( rc == DB_NOTFOUND && id == 0 ) {
172                 Entry *dummy = ch_calloc( 1, sizeof(Entry) );
173                 struct berval gluebv = BER_BVC("glue");
174                 dummy->e_name.bv_val = ch_strdup( "" );
175                 dummy->e_nname.bv_val = ch_strdup( "" );
176                 attr_merge_one( dummy, slap_schema.si_ad_objectClass, &gluebv, NULL );
177                 attr_merge_one( dummy, slap_schema.si_ad_structuralObjectClass,
178                         &gluebv, NULL );
179                 *e = dummy;
180                 rc = LDAP_SUCCESS;
181         }
182         return rc;
183 }
184
185 Entry* bdb_tool_entry_get( BackendDB *be, ID id )
186 {
187         int rc;
188         Entry *e = NULL;
189         struct berval bv;
190
191         assert( be != NULL );
192         assert( slapMode & SLAP_TOOL_MODE );
193         assert( data.data != NULL );
194
195         DBT2bv( &data, &bv );
196
197 #ifdef SLAP_ZONE_ALLOC
198         /* FIXME: will add ctx later */
199         rc = entry_decode( &bv, &e, NULL );
200 #else
201         rc = entry_decode( &bv, &e );
202 #endif
203
204         if( rc == LDAP_SUCCESS ) {
205                 e->e_id = id;
206         }
207 #ifdef BDB_HIER
208         {
209                 EntryInfo *ei = NULL;
210                 Operation op = {0};
211                 Opheader ohdr = {0};
212
213                 op.o_hdr = &ohdr;
214                 op.o_bd = be;
215                 op.o_tmpmemctx = NULL;
216                 op.o_tmpmfuncs = &ch_mfuncs;
217
218                 rc = bdb_cache_find_parent( &op, NULL, cursor->locker, id, &ei );
219                 if ( rc == LDAP_SUCCESS ) {
220                         bdb_cache_entryinfo_unlock( ei );
221                         e->e_private = ei;
222                         ei->bei_e = e;
223                         bdb_fix_dn( e, 0 );
224                         ei->bei_e = NULL;
225                         e->e_private = NULL;
226                 }
227         }
228 #endif
229         return e;
230 }
231
232 static int bdb_tool_next_id(
233         Operation *op,
234         DB_TXN *tid,
235         Entry *e,
236         struct berval *text,
237         int hole )
238 {
239         struct berval dn = e->e_name;
240         struct berval ndn = e->e_nname;
241         struct berval pdn, npdn;
242         EntryInfo *ei = NULL, eidummy;
243         int rc;
244
245         if (ndn.bv_len == 0) {
246                 e->e_id = 0;
247                 return 0;
248         }
249
250         rc = bdb_cache_find_ndn( op, tid, &ndn, &ei );
251         if ( ei ) bdb_cache_entryinfo_unlock( ei );
252         if ( rc == DB_NOTFOUND ) {
253                 if ( !be_issuffix( op->o_bd, &ndn ) ) {
254                         ID eid = e->e_id;
255                         dnParent( &dn, &pdn );
256                         dnParent( &ndn, &npdn );
257                         e->e_name = pdn;
258                         e->e_nname = npdn;
259                         rc = bdb_tool_next_id( op, tid, e, text, 1 );
260                         e->e_name = dn;
261                         e->e_nname = ndn;
262                         if ( rc ) {
263                                 return rc;
264                         }
265                         /* If parent didn't exist, it was created just now
266                          * and its ID is now in e->e_id. Make sure the current
267                          * entry gets added under the new parent ID.
268                          */
269                         if ( eid != e->e_id ) {
270                                 eidummy.bei_id = e->e_id;
271                                 ei = &eidummy;
272                         }
273                 }
274                 rc = bdb_next_id( op->o_bd, tid, &e->e_id );
275                 if ( rc ) {
276                         snprintf( text->bv_val, text->bv_len,
277                                 "next_id failed: %s (%d)",
278                                 db_strerror(rc), rc );
279                 Debug( LDAP_DEBUG_ANY,
280                         "=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
281                         return rc;
282                 }
283                 rc = bdb_dn2id_add( op, tid, ei, e );
284                 if ( rc ) {
285                         snprintf( text->bv_val, text->bv_len, 
286                                 "dn2id_add failed: %s (%d)",
287                                 db_strerror(rc), rc );
288                 Debug( LDAP_DEBUG_ANY,
289                         "=> bdb_tool_next_id: %s\n", text->bv_val, 0, 0 );
290                 } else if ( hole ) {
291                         if ( nholes == nhmax - 1 ) {
292                                 if ( holes == hbuf ) {
293                                         holes = ch_malloc( nhmax * sizeof(dn_id) * 2 );
294                                         AC_MEMCPY( holes, hbuf, sizeof(hbuf) );
295                                 } else {
296                                         holes = ch_realloc( holes, nhmax * sizeof(dn_id) * 2 );
297                                 }
298                                 nhmax *= 2;
299                         }
300                         ber_dupbv( &holes[nholes].dn, &ndn );
301                         holes[nholes++].id = e->e_id;
302                 }
303         } else if ( !hole ) {
304                 unsigned i;
305
306                 e->e_id = ei->bei_id;
307
308                 for ( i=0; i<nholes; i++) {
309                         if ( holes[i].id == e->e_id ) {
310                                 int j;
311                                 free(holes[i].dn.bv_val);
312                                 for (j=i;j<nholes;j++) holes[j] = holes[j+1];
313                                 holes[j].id = 0;
314                                 nholes--;
315                                 break;
316                         } else if ( holes[i].id > e->e_id ) {
317                                 break;
318                         }
319                 }
320         }
321         return rc;
322 }
323
324 ID bdb_tool_entry_put(
325         BackendDB *be,
326         Entry *e,
327         struct berval *text )
328 {
329         int rc;
330         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
331         DB_TXN *tid = NULL;
332         Operation op = {0};
333         Opheader ohdr = {0};
334
335         assert( be != NULL );
336         assert( slapMode & SLAP_TOOL_MODE );
337
338         assert( text != NULL );
339         assert( text->bv_val != NULL );
340         assert( text->bv_val[0] == '\0' );      /* overconservative? */
341
342         Debug( LDAP_DEBUG_TRACE, "=> " LDAP_XSTRING(bdb_tool_entry_put)
343                 "( %ld, \"%s\" )\n", (long) e->e_id, e->e_dn, 0 );
344
345         if (! (slapMode & SLAP_TOOL_QUICK)) {
346         rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &tid, 
347                 bdb->bi_db_opflags );
348         if( rc != 0 ) {
349                 snprintf( text->bv_val, text->bv_len,
350                         "txn_begin failed: %s (%d)",
351                         db_strerror(rc), rc );
352                 Debug( LDAP_DEBUG_ANY,
353                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
354                          text->bv_val, 0, 0 );
355                 return NOID;
356         }
357         }
358
359         op.o_hdr = &ohdr;
360         op.o_bd = be;
361         op.o_tmpmemctx = NULL;
362         op.o_tmpmfuncs = &ch_mfuncs;
363
364         /* add dn2id indices */
365         rc = bdb_tool_next_id( &op, tid, e, text, 0 );
366         if( rc != 0 ) {
367                 goto done;
368         }
369
370         /* id2entry index */
371         rc = bdb_id2entry_add( be, tid, e );
372         if( rc != 0 ) {
373                 snprintf( text->bv_val, text->bv_len,
374                                 "id2entry_add failed: %s (%d)",
375                                 db_strerror(rc), rc );
376                 Debug( LDAP_DEBUG_ANY,
377                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
378                         text->bv_val, 0, 0 );
379                 goto done;
380         }
381
382         if ( !bdb->bi_linear_index )
383                 rc = bdb_index_entry_add( &op, tid, e );
384         if( rc != 0 ) {
385                 snprintf( text->bv_val, text->bv_len,
386                                 "index_entry_add failed: %s (%d)",
387                                 db_strerror(rc), rc );
388                 Debug( LDAP_DEBUG_ANY,
389                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
390                         text->bv_val, 0, 0 );
391                 goto done;
392         }
393
394 done:
395         if( rc == 0 ) {
396                 if ( !( slapMode & SLAP_TOOL_QUICK )) {
397                 rc = TXN_COMMIT( tid, 0 );
398                 if( rc != 0 ) {
399                         snprintf( text->bv_val, text->bv_len,
400                                         "txn_commit failed: %s (%d)",
401                                         db_strerror(rc), rc );
402                         Debug( LDAP_DEBUG_ANY,
403                                 "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
404                                 text->bv_val, 0, 0 );
405                         e->e_id = NOID;
406                 }
407                 }
408
409         } else {
410                 if ( !( slapMode & SLAP_TOOL_QUICK )) {
411                 TXN_ABORT( tid );
412                 snprintf( text->bv_val, text->bv_len,
413                         "txn_aborted! %s (%d)",
414                         db_strerror(rc), rc );
415                 Debug( LDAP_DEBUG_ANY,
416                         "=> " LDAP_XSTRING(bdb_tool_entry_put) ": %s\n",
417                         text->bv_val, 0, 0 );
418                 }
419                 e->e_id = NOID;
420         }
421
422         return e->e_id;
423 }
424
425 int bdb_tool_entry_reindex(
426         BackendDB *be,
427         ID id )
428 {
429         struct bdb_info *bi = (struct bdb_info *) be->be_private;
430         int rc;
431         Entry *e;
432         DB_TXN *tid = NULL;
433         Operation op = {0};
434         Opheader ohdr = {0};
435
436         Debug( LDAP_DEBUG_ARGS,
437                 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) "( %ld )\n",
438                 (long) id, 0, 0 );
439
440         /* No indexes configured, nothing to do. Could return an
441          * error here to shortcut things.
442          */
443         if (!bi->bi_attrs) {
444                 return 0;
445         }
446
447         /* Get the first attribute to index */
448         if (bi->bi_linear_index && !index_attrs && bi->bi_attrs != &index_dummy) {
449                 index_attrs = bi->bi_attrs;
450                 bi->bi_attrs = &index_dummy;
451                 index_dummy.avl_data = avl_delete(&index_attrs, NULL, bdb_reindex_cmp);
452         }
453
454         e = bdb_tool_entry_get( be, id );
455
456         if( e == NULL ) {
457                 Debug( LDAP_DEBUG_ANY,
458                         LDAP_XSTRING(bdb_tool_entry_reindex)
459                         ": could not locate id=%ld\n",
460                         (long) id, 0, 0 );
461                 return -1;
462         }
463
464         if (! (slapMode & SLAP_TOOL_QUICK)) {
465         rc = TXN_BEGIN( bi->bi_dbenv, NULL, &tid, bi->bi_db_opflags );
466         if( rc != 0 ) {
467                 Debug( LDAP_DEBUG_ANY,
468                         "=> " LDAP_XSTRING(bdb_tool_entry_reindex) ": "
469                         "txn_begin failed: %s (%d)\n",
470                         db_strerror(rc), rc, 0 );
471                 goto done;
472         }
473         }
474         
475         /*
476          * just (re)add them for now
477          * assume that some other routine (not yet implemented)
478          * will zap index databases
479          *
480          */
481
482         Debug( LDAP_DEBUG_TRACE,
483                 "=> " LDAP_XSTRING(bdb_tool_entry_reindex) "( %ld, \"%s\" )\n",
484                 (long) id, e->e_dn, 0 );
485
486         op.o_hdr = &ohdr;
487         op.o_bd = be;
488         op.o_tmpmemctx = NULL;
489         op.o_tmpmfuncs = &ch_mfuncs;
490
491         rc = bdb_index_entry_add( &op, tid, e );
492
493 done:
494         if( rc == 0 ) {
495                 if (! (slapMode & SLAP_TOOL_QUICK)) {
496                 rc = TXN_COMMIT( tid, 0 );
497                 if( rc != 0 ) {
498                         Debug( LDAP_DEBUG_ANY,
499                                 "=> " LDAP_XSTRING(bdb_tool_entry_reindex)
500                                 ": txn_commit failed: %s (%d)\n",
501                                 db_strerror(rc), rc, 0 );
502                         e->e_id = NOID;
503                 }
504                 }
505
506         } else {
507                 if (! (slapMode & SLAP_TOOL_QUICK)) {
508                 TXN_ABORT( tid );
509                 Debug( LDAP_DEBUG_ANY,
510                         "=> " LDAP_XSTRING(bdb_tool_entry_reindex)
511                         ": txn_aborted! %s (%d)\n",
512                         db_strerror(rc), rc, 0 );
513                 }
514                 e->e_id = NOID;
515         }
516         bdb_entry_release( &op, e, 0 );
517
518         return rc;
519 }
520
521 ID bdb_tool_entry_modify(
522         BackendDB *be,
523         Entry *e,
524         struct berval *text )
525 {
526         int rc;
527         struct bdb_info *bdb = (struct bdb_info *) be->be_private;
528         DB_TXN *tid = NULL;
529         Operation op = {0};
530         Opheader ohdr = {0};
531
532         assert( be != NULL );
533         assert( slapMode & SLAP_TOOL_MODE );
534
535         assert( text != NULL );
536         assert( text->bv_val != NULL );
537         assert( text->bv_val[0] == '\0' );      /* overconservative? */
538
539         assert ( e->e_id != NOID );
540
541         Debug( LDAP_DEBUG_TRACE,
542                 "=> " LDAP_XSTRING(bdb_tool_entry_modify) "( %ld, \"%s\" )\n",
543                 (long) e->e_id, e->e_dn, 0 );
544
545         if (! (slapMode & SLAP_TOOL_QUICK)) {
546         rc = TXN_BEGIN( bdb->bi_dbenv, NULL, &tid, 
547                 bdb->bi_db_opflags );
548         if( rc != 0 ) {
549                 snprintf( text->bv_val, text->bv_len,
550                         "txn_begin failed: %s (%d)",
551                         db_strerror(rc), rc );
552                 Debug( LDAP_DEBUG_ANY,
553                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
554                          text->bv_val, 0, 0 );
555                 return NOID;
556         }
557         }
558
559         op.o_hdr = &ohdr;
560         op.o_bd = be;
561         op.o_tmpmemctx = NULL;
562         op.o_tmpmfuncs = &ch_mfuncs;
563
564         /* id2entry index */
565         rc = bdb_id2entry_update( be, tid, e );
566         if( rc != 0 ) {
567                 snprintf( text->bv_val, text->bv_len,
568                                 "id2entry_add failed: %s (%d)",
569                                 db_strerror(rc), rc );
570                 Debug( LDAP_DEBUG_ANY,
571                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
572                         text->bv_val, 0, 0 );
573                 goto done;
574         }
575
576 #if 0
577         /* FIXME: this is bogus, we don't have the old values to delete
578          * from the index because the given entry has already been modified.
579          */
580         rc = bdb_index_entry_del( &op, tid, e );
581         if( rc != 0 ) {
582                 snprintf( text->bv_val, text->bv_len,
583                                 "index_entry_del failed: %s (%d)",
584                                 db_strerror(rc), rc );
585                 Debug( LDAP_DEBUG_ANY,
586                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
587                         text->bv_val, 0, 0 );
588                 goto done;
589         }
590 #endif
591
592         rc = bdb_index_entry_add( &op, tid, e );
593         if( rc != 0 ) {
594                 snprintf( text->bv_val, text->bv_len,
595                                 "index_entry_add failed: %s (%d)",
596                                 db_strerror(rc), rc );
597                 Debug( LDAP_DEBUG_ANY,
598                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
599                         text->bv_val, 0, 0 );
600                 goto done;
601         }
602
603 done:
604         if( rc == 0 ) {
605                 if (! (slapMode & SLAP_TOOL_QUICK)) {
606                 rc = TXN_COMMIT( tid, 0 );
607                 if( rc != 0 ) {
608                         snprintf( text->bv_val, text->bv_len,
609                                         "txn_commit failed: %s (%d)",
610                                         db_strerror(rc), rc );
611                         Debug( LDAP_DEBUG_ANY,
612                                 "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": "
613                                 "%s\n", text->bv_val, 0, 0 );
614                         e->e_id = NOID;
615                 }
616                 }
617
618         } else {
619                 if (! (slapMode & SLAP_TOOL_QUICK)) {
620                 TXN_ABORT( tid );
621                 snprintf( text->bv_val, text->bv_len,
622                         "txn_aborted! %s (%d)",
623                         db_strerror(rc), rc );
624                 Debug( LDAP_DEBUG_ANY,
625                         "=> " LDAP_XSTRING(bdb_tool_entry_modify) ": %s\n",
626                         text->bv_val, 0, 0 );
627                 }
628                 e->e_id = NOID;
629         }
630
631         return e->e_id;
632 }