📄 bt_put.c
字号:
nbytes = B_TYPE(bk->type) == B_OVERFLOW ? ((BOVERFLOW *)bk)->tlen : bk->len; return (__db_partsize(nbytes, data));}/* * __bam_build -- * Build the real record for a partial put, or short fixed-length record. */static int__bam_build(dbc, op, dbt, h, indx, nbytes) DBC *dbc; u_int32_t op, indx, nbytes; DBT *dbt; PAGE *h;{ BKEYDATA *bk, tbk; BOVERFLOW *bo; BTREE *t; DB *dbp; DBT copy, *rdata; u_int32_t len, tlen; u_int8_t *p; int ret; COMPQUIET(bo, NULL); dbp = dbc->dbp; t = dbp->bt_internal; /* We use the record data return memory, it's only a short-term use. */ rdata = &dbc->my_rdata; if (rdata->ulen < nbytes) { if ((ret = __os_realloc(dbp->dbenv, nbytes, &rdata->data)) != 0) { rdata->ulen = 0; rdata->data = NULL; return (ret); } rdata->ulen = nbytes; } /* * We use nul or pad bytes for any part of the record that isn't * specified; get it over with. */ memset(rdata->data, F_ISSET(dbp, DB_AM_FIXEDLEN) ? t->re_pad : 0, nbytes); /* * In the next clauses, we need to do three things: a) set p to point * to the place at which to copy the user's data, b) set tlen to the * total length of the record, not including the bytes contributed by * the user, and c) copy any valid data from an existing record. If * it's not a partial put (this code is called for both partial puts * and fixed-length record padding) or it's a new key, we can cut to * the chase. */ if (!F_ISSET(dbt, DB_DBT_PARTIAL) || op != DB_CURRENT) { p = (u_int8_t *)rdata->data + dbt->doff; tlen = dbt->doff; goto user_copy; } /* Find the current record. */ if (indx < NUM_ENT(h)) { bk = GET_BKEYDATA(dbp, h, indx + (TYPE(h) == P_LBTREE ? O_INDX : 0)); bo = (BOVERFLOW *)bk; } else { bk = &tbk; B_TSET(bk->type, B_KEYDATA, 0); bk->len = 0; } if (B_TYPE(bk->type) == B_OVERFLOW) { /* * In the case of an overflow record, we shift things around * in the current record rather than allocate a separate copy. */ memset(©, 0, sizeof(copy)); if ((ret = __db_goff(dbp, ©, bo->tlen, bo->pgno, &rdata->data, &rdata->ulen)) != 0) return (ret); /* Skip any leading data from the original record. */ tlen = dbt->doff; p = (u_int8_t *)rdata->data + dbt->doff; /* * Copy in any trailing data from the original record. * * If the original record was larger than the original offset * plus the bytes being deleted, there is trailing data in the * original record we need to preserve. If we aren't deleting * the same number of bytes as we're inserting, copy it up or * down, into place. * * Use memmove(), the regions may overlap. */ if (bo->tlen > dbt->doff + dbt->dlen) { len = bo->tlen - (dbt->doff + dbt->dlen); if (dbt->dlen != dbt->size) memmove(p + dbt->size, p + dbt->dlen, len); tlen += len; } } else { /* Copy in any leading data from the original record. */ memcpy(rdata->data, bk->data, dbt->doff > bk->len ? bk->len : dbt->doff); tlen = dbt->doff; p = (u_int8_t *)rdata->data + dbt->doff; /* Copy in any trailing data from the original record. */ len = dbt->doff + dbt->dlen; if (bk->len > len) { memcpy(p + dbt->size, bk->data + len, bk->len - len); tlen += bk->len - len; } }user_copy: /* * Copy in the application provided data -- p and tlen must have been * initialized above. */ memcpy(p, dbt->data, dbt->size); tlen += dbt->size; /* Set the DBT to reference our new record. */ rdata->size = F_ISSET(dbp, DB_AM_FIXEDLEN) ? t->re_len : tlen; rdata->dlen = 0; rdata->doff = 0; rdata->flags = 0; *dbt = *rdata; return (0);}/* * __bam_ritem -- * Replace an item on a page. * * PUBLIC: int __bam_ritem __P((DBC *, PAGE *, u_int32_t, DBT *)); */int__bam_ritem(dbc, h, indx, data) DBC *dbc; PAGE *h; u_int32_t indx; DBT *data;{ BKEYDATA *bk; DB *dbp; DBT orig, repl; db_indx_t cnt, lo, ln, min, off, prefix, suffix; int32_t nbytes; int ret; db_indx_t *inp; u_int8_t *p, *t; dbp = dbc->dbp; /* * Replace a single item onto a page. The logic figuring out where * to insert and whether it fits is handled in the caller. All we do * here is manage the page shuffling. */ bk = GET_BKEYDATA(dbp, h, indx); /* Log the change. */ if (DBC_LOGGING(dbc)) { /* * We might as well check to see if the two data items share * a common prefix and suffix -- it can save us a lot of log * message if they're large. */ min = data->size < bk->len ? data->size : bk->len; for (prefix = 0, p = bk->data, t = data->data; prefix < min && *p == *t; ++prefix, ++p, ++t) ; min -= prefix; for (suffix = 0, p = (u_int8_t *)bk->data + bk->len - 1, t = (u_int8_t *)data->data + data->size - 1; suffix < min && *p == *t; ++suffix, --p, --t) ; /* We only log the parts of the keys that have changed. */ orig.data = (u_int8_t *)bk->data + prefix; orig.size = bk->len - (prefix + suffix); repl.data = (u_int8_t *)data->data + prefix; repl.size = data->size - (prefix + suffix); if ((ret = __bam_repl_log(dbp, dbc->txn, &LSN(h), 0, PGNO(h), &LSN(h), (u_int32_t)indx, (u_int32_t)B_DISSET(bk->type), &orig, &repl, (u_int32_t)prefix, (u_int32_t)suffix)) != 0) return (ret); } else LSN_NOT_LOGGED(LSN(h)); /* * Set references to the first in-use byte on the page and the * first byte of the item being replaced. */ inp = P_INP(dbp, h); p = (u_int8_t *)h + HOFFSET(h); t = (u_int8_t *)bk; /* * If the entry is growing in size, shift the beginning of the data * part of the page down. If the entry is shrinking in size, shift * the beginning of the data part of the page up. Use memmove(3), * the regions overlap. */ lo = BKEYDATA_SIZE(bk->len); ln = (db_indx_t)BKEYDATA_SIZE(data->size); if (lo != ln) { nbytes = lo - ln; /* Signed difference. */ if (p == t) /* First index is fast. */ inp[indx] += nbytes; else { /* Else, shift the page. */ memmove(p + nbytes, p, t - p); /* Adjust the indices' offsets. */ off = inp[indx]; for (cnt = 0; cnt < NUM_ENT(h); ++cnt) if (inp[cnt] <= off) inp[cnt] += nbytes; } /* Clean up the page and adjust the item's reference. */ HOFFSET(h) += nbytes; t += nbytes; } /* Copy the new item onto the page. */ bk = (BKEYDATA *)t; B_TSET(bk->type, B_KEYDATA, 0); bk->len = data->size; memcpy(bk->data, data->data, data->size); return (0);}/* * __bam_dup_convert -- * Check to see if the duplicate set at indx should have its own page. * If it should, create it. */static int__bam_dup_convert(dbc, h, indx) DBC *dbc; PAGE *h; u_int32_t indx;{ BKEYDATA *bk; DB *dbp; DBT hdr; DB_MPOOLFILE *mpf; PAGE *dp; db_indx_t cnt, cpindx, dindx, first, *inp, sz; int ret; dbp = dbc->dbp; mpf = dbp->mpf; inp = P_INP(dbp, h); /* * Count the duplicate records and calculate how much room they're * using on the page. */ while (indx > 0 && inp[indx] == inp[indx - P_INDX]) indx -= P_INDX; for (cnt = 0, sz = 0, first = indx;; ++cnt, indx += P_INDX) { if (indx >= NUM_ENT(h) || inp[first] != inp[indx]) break; bk = GET_BKEYDATA(dbp, h, indx); sz += B_TYPE(bk->type) == B_KEYDATA ? BKEYDATA_PSIZE(bk->len) : BOVERFLOW_PSIZE; bk = GET_BKEYDATA(dbp, h, indx + O_INDX); sz += B_TYPE(bk->type) == B_KEYDATA ? BKEYDATA_PSIZE(bk->len) : BOVERFLOW_PSIZE; } /* * We have to do these checks when the user is replacing the cursor's * data item -- if the application replaces a duplicate item with a * larger data item, it can increase the amount of space used by the * duplicates, requiring this check. But that means we may have done * this check when it wasn't a duplicate item after all. */ if (cnt == 1) return (0); /* * If this set of duplicates is using more than 25% of the page, move * them off. The choice of 25% is a WAG, but the value must be small * enough that we can always split a page without putting duplicates * on two different pages. */ if (sz < dbp->pgsize / 4) return (0); /* Get a new page. */ if ((ret = __db_new(dbc, dbp->dup_compare == NULL ? P_LRECNO : P_LDUP, &dp)) != 0) return (ret); P_INIT(dp, dbp->pgsize, dp->pgno, PGNO_INVALID, PGNO_INVALID, LEAFLEVEL, TYPE(dp)); /* * Move this set of duplicates off the page. First points to the first * key of the first duplicate key/data pair, cnt is the number of pairs * we're dealing with. */ memset(&hdr, 0, sizeof(hdr)); dindx = first; indx = first; cpindx = 0; do { /* Move cursors referencing the old entry to the new entry. */ if ((ret = __bam_ca_dup(dbc, first, PGNO(h), indx, PGNO(dp), cpindx)) != 0) goto err; /* * Copy the entry to the new page. If the off-duplicate page * If the off-duplicate page is a Btree page (i.e. dup_compare * will be non-NULL, we use Btree pages for sorted dups, * and Recno pages for unsorted dups), move all entries * normally, even deleted ones. If it's a Recno page, * deleted entries are discarded (if the deleted entry is * overflow, then free up those pages). */ bk = GET_BKEYDATA(dbp, h, dindx + 1); hdr.data = bk; hdr.size = B_TYPE(bk->type) == B_KEYDATA ? BKEYDATA_SIZE(bk->len) : BOVERFLOW_SIZE; if (dbp->dup_compare == NULL && B_DISSET(bk->type)) { /* * Unsorted dups, i.e. recno page, and we have * a deleted entry, don't move it, but if it was * an overflow entry, we need to free those pages. */ if (B_TYPE(bk->type) == B_OVERFLOW && (ret = __db_doff(dbc, (GET_BOVERFLOW(dbp, h, dindx + 1))->pgno)) != 0) goto err; } else { if ((ret = __db_pitem( dbc, dp, cpindx, hdr.size, &hdr, NULL)) != 0) goto err; ++cpindx; } /* Delete all but the last reference to the key. */ if (cnt != 1) { if ((ret = __bam_adjindx(dbc, h, dindx, first + 1, 0)) != 0) goto err; } else dindx++; /* Delete the data item. */ if ((ret = __db_ditem(dbc, h, dindx, hdr.size)) != 0) goto err; indx += P_INDX; } while (--cnt); /* Put in a new data item that points to the duplicates page. */ if ((ret = __bam_ovput(dbc, B_DUPLICATE, dp->pgno, h, first + 1, NULL)) != 0) goto err; /* Adjust cursors for all the above movments. */ if ((ret = __bam_ca_di(dbc, PGNO(h), first + P_INDX, first + P_INDX - indx)) != 0) goto err; return (mpf->put(mpf, dp, DB_MPOOL_DIRTY));err: (void)mpf->put(mpf, dp, 0); return (ret);}/* * __bam_ovput -- * Build an item for an off-page duplicates page or overflow page and * insert it on the page. */static int__bam_ovput(dbc, type, pgno, h, indx, item) DBC *dbc; u_int32_t type, indx; db_pgno_t pgno; PAGE *h; DBT *item;{ BOVERFLOW bo; DBT hdr; int ret; UMRW_SET(bo.unused1); B_TSET(bo.type, type, 0); UMRW_SET(bo.unused2); /* * If we're creating an overflow item, do so and acquire the page * number for it. If we're creating an off-page duplicates tree, * we are giving the page number as an argument. */ if (type == B_OVERFLOW) { if ((ret = __db_poff(dbc, item, &bo.pgno)) != 0) return (ret); bo.tlen = item->size; } else { bo.pgno = pgno; bo.tlen = 0; } /* Store the new record on the page. */ memset(&hdr, 0, sizeof(hdr)); hdr.data = &bo; hdr.size = BOVERFLOW_SIZE; return (__db_pitem(dbc, h, indx, BOVERFLOW_SIZE, &hdr, NULL));}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -