source: trunk/third/evolution-data-server/libdb/btree/bt_curadj.c @ 21189

Revision 21189, 14.9 KB checked in by ghudson, 20 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r21188, which included commits to RCS files with non-trunk default branches.
Line 
1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 1996-2002
5 *      Sleepycat Software.  All rights reserved.
6 */
7
8#include "db_config.h"
9
10#ifndef lint
11static const char revid[] = "$Id: bt_curadj.c,v 1.1.1.1 2004-12-17 17:26:42 ghudson Exp $";
12#endif /* not lint */
13
14#ifndef NO_SYSTEM_INCLUDES
15#include <sys/types.h>
16#endif
17
18#include "db_int.h"
19#include "dbinc/db_page.h"
20#include "dbinc/btree.h"
21
22static int __bam_opd_cursor __P((DB *, DBC *, db_pgno_t, u_int32_t, u_int32_t));
23
24#ifdef DEBUG
25/*
26 * __bam_cprint --
27 *      Display the current internal cursor.
28 *
29 * PUBLIC: void __bam_cprint __P((DBC *));
30 */
31void
32__bam_cprint(dbc)
33        DBC *dbc;
34{
35        BTREE_CURSOR *cp;
36
37        cp = (BTREE_CURSOR *)dbc->internal;
38
39        fprintf(stderr, "\tinternal: ovflsize: %lu", (u_long)cp->ovflsize);
40        if (dbc->dbtype == DB_RECNO)
41                fprintf(stderr, " recno: %lu", (u_long)cp->recno);
42        if (F_ISSET(cp, C_DELETED))
43                fprintf(stderr, " (deleted)");
44        fprintf(stderr, "\n");
45}
46#endif
47
48/*
49 * Cursor adjustments are logged if they are for subtransactions.  This is
50 * because it's possible for a subtransaction to adjust cursors which will
51 * still be active after the subtransaction aborts, and so which must be
52 * restored to their previous locations.  Cursors that can be both affected
53 * by our cursor adjustments and active after our transaction aborts can
54 * only be found in our parent transaction -- cursors in other transactions,
55 * including other child transactions of our parent, must have conflicting
56 * locker IDs, and so cannot be affected by adjustments in this transaction.
57 */
58
59/*
60 * __bam_ca_delete --
61 *      Update the cursors when items are deleted and when already deleted
62 *      items are overwritten.  Return the number of relevant cursors found.
63 *
64 * PUBLIC: int __bam_ca_delete __P((DB *, db_pgno_t, u_int32_t, int));
65 */
66int
67__bam_ca_delete(dbp, pgno, indx, delete)
68        DB *dbp;
69        db_pgno_t pgno;
70        u_int32_t indx;
71        int delete;
72{
73        BTREE_CURSOR *cp;
74        DB *ldbp;
75        DB_ENV *dbenv;
76        DBC *dbc;
77        int count;              /* !!!: Has to contain max number of cursors. */
78
79        dbenv = dbp->dbenv;
80
81        /*
82         * Adjust the cursors.  We have the page write locked, so the
83         * only other cursors that can be pointing at a page are
84         * those in the same thread of control.  Unfortunately, we don't
85         * know that they're using the same DB handle, so traverse
86         * all matching DB handles in the same DB_ENV, then all cursors
87         * on each matching DB handle.
88         *
89         * Each cursor is single-threaded, so we only need to lock the
90         * list of DBs and then the list of cursors in each DB.
91         */
92        MUTEX_THREAD_LOCK(dbenv, dbenv->dblist_mutexp);
93        for (count = 0, ldbp = __dblist_get(dbenv, dbp->adj_fileid);
94            ldbp != NULL && ldbp->adj_fileid == dbp->adj_fileid;
95            ldbp = LIST_NEXT(ldbp, dblistlinks)) {
96                MUTEX_THREAD_LOCK(dbenv, dbp->mutexp);
97                for (dbc = TAILQ_FIRST(&ldbp->active_queue);
98                    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
99                        cp = (BTREE_CURSOR *)dbc->internal;
100                        if (cp->pgno == pgno && cp->indx == indx) {
101                                if (delete)
102                                        F_SET(cp, C_DELETED);
103                                else
104                                        F_CLR(cp, C_DELETED);
105                                ++count;
106                        }
107                }
108                MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp);
109        }
110        MUTEX_THREAD_UNLOCK(dbenv, dbenv->dblist_mutexp);
111
112        return (count);
113}
114
115/*
116 * __ram_ca_delete --
117 *      Return the number of relevant cursors.
118 *
119 * PUBLIC: int __ram_ca_delete __P((DB *, db_pgno_t));
120 */
121int
122__ram_ca_delete(dbp, root_pgno)
123        DB *dbp;
124        db_pgno_t root_pgno;
125{
126        DB *ldbp;
127        DBC *dbc;
128        DB_ENV *dbenv;
129        int found;
130
131        found = 0;
132        dbenv = dbp->dbenv;
133
134        /*
135         * Review the cursors.  See the comment in __bam_ca_delete().
136         */
137        MUTEX_THREAD_LOCK(dbenv, dbenv->dblist_mutexp);
138        for (ldbp = __dblist_get(dbenv, dbp->adj_fileid);
139            found == 0 && ldbp != NULL && ldbp->adj_fileid == dbp->adj_fileid;
140            ldbp = LIST_NEXT(ldbp, dblistlinks)) {
141                MUTEX_THREAD_LOCK(dbenv, dbp->mutexp);
142                for (dbc = TAILQ_FIRST(&ldbp->active_queue);
143                    found == 0 && dbc != NULL; dbc = TAILQ_NEXT(dbc, links))
144                        if (dbc->internal->root == root_pgno)
145                                found = 1;
146                MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp);
147        }
148        MUTEX_THREAD_UNLOCK(dbenv, dbenv->dblist_mutexp);
149        return (found);
150}
151
152/*
153 * __bam_ca_di --
154 *      Adjust the cursors during a delete or insert.
155 *
156 * PUBLIC: int __bam_ca_di __P((DBC *, db_pgno_t, u_int32_t, int));
157 */
158int
159__bam_ca_di(my_dbc, pgno, indx, adjust)
160        DBC *my_dbc;
161        db_pgno_t pgno;
162        u_int32_t indx;
163        int adjust;
164{
165        DB *dbp, *ldbp;
166        DB_ENV *dbenv;
167        DB_LSN lsn;
168        DB_TXN *my_txn;
169        DBC *dbc;
170        DBC_INTERNAL *cp;
171        int found, ret;
172
173        dbp = my_dbc->dbp;
174        dbenv = dbp->dbenv;
175
176        my_txn = IS_SUBTRANSACTION(my_dbc->txn) ? my_dbc->txn : NULL;
177
178        /*
179         * Adjust the cursors.  See the comment in __bam_ca_delete().
180         */
181        found = 0;
182        MUTEX_THREAD_LOCK(dbenv, dbenv->dblist_mutexp);
183        for (ldbp = __dblist_get(dbenv, dbp->adj_fileid);
184            ldbp != NULL && ldbp->adj_fileid == dbp->adj_fileid;
185            ldbp = LIST_NEXT(ldbp, dblistlinks)) {
186                MUTEX_THREAD_LOCK(dbenv, dbp->mutexp);
187                for (dbc = TAILQ_FIRST(&ldbp->active_queue);
188                    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
189                        if (dbc->dbtype == DB_RECNO)
190                                continue;
191                        cp = dbc->internal;
192                        if (cp->pgno == pgno && cp->indx >= indx) {
193                                /* Cursor indices should never be negative. */
194                                DB_ASSERT(cp->indx != 0 || adjust > 0);
195
196                                cp->indx += adjust;
197                                if (my_txn != NULL && dbc->txn != my_txn)
198                                        found = 1;
199                        }
200                }
201                MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp);
202        }
203        MUTEX_THREAD_UNLOCK(dbenv, dbenv->dblist_mutexp);
204
205        if (found != 0 && DBC_LOGGING(my_dbc)) {
206                if ((ret = __bam_curadj_log(dbp, my_dbc->txn,
207                    &lsn, 0, DB_CA_DI, pgno, 0, 0, adjust, indx, 0)) != 0)
208                        return (ret);
209        }
210
211        return (0);
212}
213
214/*
215 * __bam_opd_cursor -- create a new opd cursor.
216 */
217static int
218__bam_opd_cursor(dbp, dbc, first, tpgno, ti)
219        DB *dbp;
220        DBC *dbc;
221        db_pgno_t tpgno;
222        u_int32_t first, ti;
223{
224        BTREE_CURSOR *cp, *orig_cp;
225        DBC *dbc_nopd;
226        int ret;
227
228        orig_cp = (BTREE_CURSOR *)dbc->internal;
229        dbc_nopd = NULL;
230
231        /*
232         * Allocate a new cursor and create the stack.  If duplicates
233         * are sorted, we've just created an off-page duplicate Btree.
234         * If duplicates aren't sorted, we've just created a Recno tree.
235         *
236         * Note that in order to get here at all, there shouldn't be
237         * an old off-page dup cursor--to augment the checking db_c_newopd
238         * will do, assert this.
239         */
240        DB_ASSERT(orig_cp->opd == NULL);
241        if ((ret = __db_c_newopd(dbc, tpgno, orig_cp->opd, &dbc_nopd)) != 0)
242                return (ret);
243
244        cp = (BTREE_CURSOR *)dbc_nopd->internal;
245        cp->pgno = tpgno;
246        cp->indx = ti;
247
248        if (dbp->dup_compare == NULL) {
249                /*
250                 * Converting to off-page Recno trees is tricky.  The
251                 * record number for the cursor is the index + 1 (to
252                 * convert to 1-based record numbers).
253                 */
254                cp->recno = ti + 1;
255        }
256
257        /*
258         * Transfer the deleted flag from the top-level cursor to the
259         * created one.
260         */
261        if (F_ISSET(orig_cp, C_DELETED)) {
262                F_SET(cp, C_DELETED);
263                F_CLR(orig_cp, C_DELETED);
264        }
265
266        /* Stack the cursors and reset the initial cursor's index. */
267        orig_cp->opd = dbc_nopd;
268        orig_cp->indx = first;
269        return (0);
270}
271
272/*
273 * __bam_ca_dup --
274 *      Adjust the cursors when moving items from a leaf page to a duplicates
275 *      page.
276 *
277 * PUBLIC: int __bam_ca_dup __P((DBC *,
278 * PUBLIC:    u_int32_t, db_pgno_t, u_int32_t, db_pgno_t, u_int32_t));
279 */
280int
281__bam_ca_dup(my_dbc, first, fpgno, fi, tpgno, ti)
282        DBC *my_dbc;
283        db_pgno_t fpgno, tpgno;
284        u_int32_t first, fi, ti;
285{
286        BTREE_CURSOR *orig_cp;
287        DB *dbp, *ldbp;
288        DBC *dbc;
289        DB_ENV *dbenv;
290        DB_LSN lsn;
291        DB_TXN *my_txn;
292        int found, ret;
293
294        dbp = my_dbc->dbp;
295        dbenv = dbp->dbenv;
296        my_txn = IS_SUBTRANSACTION(my_dbc->txn) ? my_dbc->txn : NULL;
297
298        /*
299         * Adjust the cursors.  See the comment in __bam_ca_delete().
300         */
301        found = 0;
302        MUTEX_THREAD_LOCK(dbenv, dbenv->dblist_mutexp);
303        for (ldbp = __dblist_get(dbenv, dbp->adj_fileid);
304            ldbp != NULL && ldbp->adj_fileid == dbp->adj_fileid;
305            ldbp = LIST_NEXT(ldbp, dblistlinks)) {
306loop:           MUTEX_THREAD_LOCK(dbenv, dbp->mutexp);
307                for (dbc = TAILQ_FIRST(&ldbp->active_queue);
308                    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
309                        /* Find cursors pointing to this record. */
310                        orig_cp = (BTREE_CURSOR *)dbc->internal;
311                        if (orig_cp->pgno != fpgno || orig_cp->indx != fi)
312                                continue;
313
314                        /*
315                         * Since we rescan the list see if this is already
316                         * converted.
317                         */
318                        if (orig_cp->opd != NULL)
319                                continue;
320
321                        MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp);
322                        if ((ret = __bam_opd_cursor(dbp,
323                            dbc, first, tpgno, ti)) !=0)
324                                return (ret);
325                        if (my_txn != NULL && dbc->txn != my_txn)
326                                found = 1;
327                        /* We released the mutex to get a cursor, start over. */
328                        goto loop;
329                }
330                MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp);
331        }
332        MUTEX_THREAD_UNLOCK(dbenv, dbenv->dblist_mutexp);
333
334        if (found != 0 && DBC_LOGGING(my_dbc)) {
335                if ((ret = __bam_curadj_log(dbp, my_dbc->txn,
336                    &lsn, 0, DB_CA_DUP, fpgno, tpgno, 0, first, fi, ti)) != 0)
337                        return (ret);
338        }
339        return (0);
340}
341
342/*
343 * __bam_ca_undodup --
344 *      Adjust the cursors when returning items to a leaf page
345 *      from a duplicate page.
346 *      Called only during undo processing.
347 *
348 * PUBLIC: int __bam_ca_undodup __P((DB *,
349 * PUBLIC:    u_int32_t, db_pgno_t, u_int32_t, u_int32_t));
350 */
351int
352__bam_ca_undodup(dbp, first, fpgno, fi, ti)
353        DB *dbp;
354        db_pgno_t fpgno;
355        u_int32_t first, fi, ti;
356{
357        BTREE_CURSOR *orig_cp;
358        DB *ldbp;
359        DBC *dbc;
360        DB_ENV *dbenv;
361        int ret;
362
363        dbenv = dbp->dbenv;
364
365        /*
366         * Adjust the cursors.  See the comment in __bam_ca_delete().
367         */
368        MUTEX_THREAD_LOCK(dbenv, dbenv->dblist_mutexp);
369        for (ldbp = __dblist_get(dbenv, dbp->adj_fileid);
370            ldbp != NULL && ldbp->adj_fileid == dbp->adj_fileid;
371            ldbp = LIST_NEXT(ldbp, dblistlinks)) {
372loop:           MUTEX_THREAD_LOCK(dbenv, dbp->mutexp);
373                for (dbc = TAILQ_FIRST(&ldbp->active_queue);
374                    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
375                        orig_cp = (BTREE_CURSOR *)dbc->internal;
376
377                        /*
378                         * A note on the orig_cp->opd != NULL requirement here:
379                         * it's possible that there's a cursor that refers to
380                         * the same duplicate set, but which has no opd cursor,
381                         * because it refers to a different item and we took
382                         * care of it while processing a previous record.
383                         */
384                        if (orig_cp->pgno != fpgno ||
385                            orig_cp->indx != first ||
386                            orig_cp->opd == NULL ||
387                            ((BTREE_CURSOR *)orig_cp->opd->internal)->indx
388                            != ti)
389                                continue;
390                        MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp);
391                        if ((ret = orig_cp->opd->c_close(orig_cp->opd)) != 0)
392                                return (ret);
393                        orig_cp->opd = NULL;
394                        orig_cp->indx = fi;
395                        /*
396                         * We released the mutex to free a cursor,
397                         * start over.
398                         */
399                        goto loop;
400                }
401                MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp);
402        }
403        MUTEX_THREAD_UNLOCK(dbenv, dbenv->dblist_mutexp);
404
405        return (0);
406}
407
408/*
409 * __bam_ca_rsplit --
410 *      Adjust the cursors when doing reverse splits.
411 *
412 * PUBLIC: int __bam_ca_rsplit __P((DBC *, db_pgno_t, db_pgno_t));
413 */
414int
415__bam_ca_rsplit(my_dbc, fpgno, tpgno)
416        DBC* my_dbc;
417        db_pgno_t fpgno, tpgno;
418{
419        DB *dbp, *ldbp;
420        DBC *dbc;
421        DB_ENV *dbenv;
422        DB_LSN lsn;
423        DB_TXN *my_txn;
424        int found, ret;
425
426        dbp = my_dbc->dbp;
427        dbenv = dbp->dbenv;
428        my_txn = IS_SUBTRANSACTION(my_dbc->txn) ? my_dbc->txn : NULL;
429
430        /*
431         * Adjust the cursors.  See the comment in __bam_ca_delete().
432         */
433        found = 0;
434        MUTEX_THREAD_LOCK(dbenv, dbenv->dblist_mutexp);
435        for (ldbp = __dblist_get(dbenv, dbp->adj_fileid);
436            ldbp != NULL && ldbp->adj_fileid == dbp->adj_fileid;
437            ldbp = LIST_NEXT(ldbp, dblistlinks)) {
438                MUTEX_THREAD_LOCK(dbenv, dbp->mutexp);
439                for (dbc = TAILQ_FIRST(&ldbp->active_queue);
440                    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
441                        if (dbc->dbtype == DB_RECNO)
442                                continue;
443                        if (dbc->internal->pgno == fpgno) {
444                                dbc->internal->pgno = tpgno;
445                                if (my_txn != NULL && dbc->txn != my_txn)
446                                        found = 1;
447                        }
448                }
449                MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp);
450        }
451        MUTEX_THREAD_UNLOCK(dbenv, dbenv->dblist_mutexp);
452
453        if (found != 0 && DBC_LOGGING(my_dbc)) {
454                if ((ret = __bam_curadj_log(dbp, my_dbc->txn,
455                    &lsn, 0, DB_CA_RSPLIT, fpgno, tpgno, 0, 0, 0, 0)) != 0)
456                        return (ret);
457        }
458        return (0);
459}
460
461/*
462 * __bam_ca_split --
463 *      Adjust the cursors when splitting a page.
464 *
465 * PUBLIC: int __bam_ca_split __P((DBC *,
466 * PUBLIC:    db_pgno_t, db_pgno_t, db_pgno_t, u_int32_t, int));
467 */
468int
469__bam_ca_split(my_dbc, ppgno, lpgno, rpgno, split_indx, cleft)
470        DBC *my_dbc;
471        db_pgno_t ppgno, lpgno, rpgno;
472        u_int32_t split_indx;
473        int cleft;
474{
475        DB *dbp, *ldbp;
476        DBC *dbc;
477        DBC_INTERNAL *cp;
478        DB_ENV *dbenv;
479        DB_LSN lsn;
480        DB_TXN *my_txn;
481        int found, ret;
482
483        dbp = my_dbc->dbp;
484        dbenv = dbp->dbenv;
485        my_txn = IS_SUBTRANSACTION(my_dbc->txn) ? my_dbc->txn : NULL;
486
487        /*
488         * Adjust the cursors.  See the comment in __bam_ca_delete().
489         *
490         * If splitting the page that a cursor was on, the cursor has to be
491         * adjusted to point to the same record as before the split.  Most
492         * of the time we don't adjust pointers to the left page, because
493         * we're going to copy its contents back over the original page.  If
494         * the cursor is on the right page, it is decremented by the number of
495         * records split to the left page.
496         */
497        found = 0;
498        MUTEX_THREAD_LOCK(dbenv, dbenv->dblist_mutexp);
499        for (ldbp = __dblist_get(dbenv, dbp->adj_fileid);
500            ldbp != NULL && ldbp->adj_fileid == dbp->adj_fileid;
501            ldbp = LIST_NEXT(ldbp, dblistlinks)) {
502                MUTEX_THREAD_LOCK(dbenv, dbp->mutexp);
503                for (dbc = TAILQ_FIRST(&ldbp->active_queue);
504                    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
505                        if (dbc->dbtype == DB_RECNO)
506                                continue;
507                        cp = dbc->internal;
508                        if (cp->pgno == ppgno) {
509                                if (my_txn != NULL && dbc->txn != my_txn)
510                                        found = 1;
511                                if (cp->indx < split_indx) {
512                                        if (cleft)
513                                                cp->pgno = lpgno;
514                                } else {
515                                        cp->pgno = rpgno;
516                                        cp->indx -= split_indx;
517                                }
518                        }
519                }
520                MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp);
521        }
522        MUTEX_THREAD_UNLOCK(dbenv, dbenv->dblist_mutexp);
523
524        if (found != 0 && DBC_LOGGING(my_dbc)) {
525                if ((ret = __bam_curadj_log(dbp,
526                    my_dbc->txn, &lsn, 0, DB_CA_SPLIT, ppgno, rpgno,
527                    cleft ? lpgno : PGNO_INVALID, 0, split_indx, 0)) != 0)
528                        return (ret);
529        }
530
531        return (0);
532}
533
534/*
535 * __bam_ca_undosplit --
536 *      Adjust the cursors when undoing a split of a page.
537 *      If we grew a level we will execute this for both the
538 *      left and the right pages.
539 *      Called only during undo processing.
540 *
541 * PUBLIC: void __bam_ca_undosplit __P((DB *,
542 * PUBLIC:    db_pgno_t, db_pgno_t, db_pgno_t, u_int32_t));
543 */
544void
545__bam_ca_undosplit(dbp, frompgno, topgno, lpgno, split_indx)
546        DB *dbp;
547        db_pgno_t frompgno, topgno, lpgno;
548        u_int32_t split_indx;
549{
550        DB *ldbp;
551        DBC *dbc;
552        DB_ENV *dbenv;
553        DBC_INTERNAL *cp;
554
555        dbenv = dbp->dbenv;
556
557        /*
558         * Adjust the cursors.  See the comment in __bam_ca_delete().
559         *
560         * When backing out a split, we move the cursor back
561         * to the original offset and bump it by the split_indx.
562         */
563        MUTEX_THREAD_LOCK(dbenv, dbenv->dblist_mutexp);
564        for (ldbp = __dblist_get(dbenv, dbp->adj_fileid);
565            ldbp != NULL && ldbp->adj_fileid == dbp->adj_fileid;
566            ldbp = LIST_NEXT(ldbp, dblistlinks)) {
567                MUTEX_THREAD_LOCK(dbenv, dbp->mutexp);
568                for (dbc = TAILQ_FIRST(&ldbp->active_queue);
569                    dbc != NULL; dbc = TAILQ_NEXT(dbc, links)) {
570                        if (dbc->dbtype == DB_RECNO)
571                                continue;
572                        cp = dbc->internal;
573                        if (cp->pgno == topgno) {
574                                cp->pgno = frompgno;
575                                cp->indx += split_indx;
576                        } else if (cp->pgno == lpgno)
577                                cp->pgno = frompgno;
578                }
579                MUTEX_THREAD_UNLOCK(dbenv, dbp->mutexp);
580        }
581        MUTEX_THREAD_UNLOCK(dbenv, dbenv->dblist_mutexp);
582}
Note: See TracBrowser for help on using the repository browser.