📄 bt_recno.c
字号:
__db_err(dbp->dbenv, "illegal record number of 0"); return (EINVAL); } if (rep != NULL) *rep = recno; /* * Btree can neither create records nor read them in. Recno can * do both, see if we can find the record. */ return (dbc->dbtype == DB_RECNO ? __ram_update(dbc, recno, can_create) : 0);}/* * __ram_update -- * Ensure the tree has records up to and including the specified one. */static int__ram_update(dbc, recno, can_create) DBC *dbc; db_recno_t recno; int can_create;{ BTREE *t; DB *dbp; DBT *rdata; db_recno_t nrecs; int ret; dbp = dbc->dbp; t = dbp->bt_internal; /* * If we can't create records and we've read the entire backing input * file, we're done. */ if (!can_create && t->re_eof) return (0); /* * If we haven't seen this record yet, try to get it from the original * file. */ if ((ret = __bam_nrecs(dbc, &nrecs)) != 0) return (ret); if (!t->re_eof && recno > nrecs) { if ((ret = __ram_sread(dbc, recno)) != 0 && ret != DB_NOTFOUND) return (ret); if ((ret = __bam_nrecs(dbc, &nrecs)) != 0) return (ret); } /* * If we can create records, create empty ones up to the requested * record. */ if (!can_create || recno <= nrecs + 1) return (0); rdata = &dbc->my_rdata; rdata->flags = 0; rdata->size = 0; while (recno > ++nrecs) if ((ret = __ram_add(dbc, &nrecs, rdata, 0, BI_DELETED)) != 0) return (ret); return (0);}/* * __ram_source -- * Load information about the backing file. */static int__ram_source(dbp) DB *dbp;{ BTREE *t; char *source; int ret; t = dbp->bt_internal; /* Find the real name, and swap out the one we had before. */ if ((ret = __db_appname(dbp->dbenv, DB_APP_DATA, t->re_source, 0, NULL, &source)) != 0) return (ret); __os_free(dbp->dbenv, t->re_source); t->re_source = source; /* * !!! * It's possible that the backing source file is read-only. We don't * much care other than we'll complain if there are any modifications * when it comes time to write the database back to the source. */ if ((t->re_fp = fopen(t->re_source, "r")) == NULL) { ret = errno; __db_err(dbp->dbenv, "%s: %s", t->re_source, db_strerror(ret)); return (ret); } t->re_eof = 0; return (0);}/* * __ram_writeback -- * Rewrite the backing file. * * PUBLIC: int __ram_writeback __P((DB *)); */int__ram_writeback(dbp) DB *dbp;{ BTREE *t; DB_ENV *dbenv; DBC *dbc; DBT key, data; FILE *fp; db_recno_t keyno; int ret, t_ret; u_int8_t delim, *pad; t = dbp->bt_internal; dbenv = dbp->dbenv; fp = NULL; pad = NULL; /* If the file wasn't modified, we're done. */ if (!t->re_modified) return (0); /* If there's no backing source file, we're done. */ if (t->re_source == NULL) { t->re_modified = 0; return (0); } /* Allocate a cursor. */ if ((ret = dbp->cursor(dbp, NULL, &dbc, 0)) != 0) return (ret); /* * Read any remaining records into the tree. * * !!! * This is why we can't support transactions when applications specify * backing (re_source) files. At this point we have to read in the * rest of the records from the file so that we can write all of the * records back out again, which could modify a page for which we'd * have to log changes and which we don't have locked. This could be * partially fixed by taking a snapshot of the entire file during the * DB->open as DB->open is transaction protected. But, if a checkpoint * occurs then, the part of the log holding the copy of the file could * be discarded, and that would make it impossible to recover in the * face of disaster. This could all probably be fixed, but it would * require transaction protecting the backing source file. * * XXX * This could be made to work now that we have transactions protecting * file operations. Margo has specifically asked for the privilege of * doing this work. */ if ((ret = __ram_update(dbc, DB_MAX_RECORDS, 0)) != 0 && ret != DB_NOTFOUND) return (ret); /* * Close any existing file handle and re-open the file, truncating it. */ if (t->re_fp != NULL) { if (fclose(t->re_fp) != 0) { ret = errno; goto err; } t->re_fp = NULL; } if ((fp = fopen(t->re_source, "w")) == NULL) { ret = errno; __db_err(dbenv, "%s: %s", t->re_source, db_strerror(ret)); goto err; } /* * We step through the records, writing each one out. Use the record * number and the dbp->get() function, instead of a cursor, so we find * and write out "deleted" or non-existent records. The DB handle may * be threaded, so allocate memory as we go. */ memset(&key, 0, sizeof(key)); key.size = sizeof(db_recno_t); key.data = &keyno; memset(&data, 0, sizeof(data)); F_SET(&data, DB_DBT_REALLOC); /* * We'll need the delimiter if we're doing variable-length records, * and the pad character if we're doing fixed-length records. */ delim = t->re_delim; if (F_ISSET(dbp, DB_AM_FIXEDLEN)) { if ((ret = __os_malloc(dbenv, t->re_len, &pad)) != 0) goto err; memset(pad, t->re_pad, t->re_len); } for (keyno = 1;; ++keyno) { switch (ret = dbp->get(dbp, NULL, &key, &data, 0)) { case 0: if (data.size != 0 && (u_int32_t)fwrite( data.data, 1, data.size, fp) != data.size) goto write_err; break; case DB_KEYEMPTY: if (F_ISSET(dbp, DB_AM_FIXEDLEN) && (u_int32_t)fwrite(pad, 1, t->re_len, fp) != t->re_len) goto write_err; break; case DB_NOTFOUND: ret = 0; goto done; default: goto err; } if (!F_ISSET(dbp, DB_AM_FIXEDLEN) && fwrite(&delim, 1, 1, fp) != 1) {write_err: ret = errno; __db_err(dbp->dbenv, "%s: write failed to backing file: %s", t->re_source, strerror(ret)); goto err; } }err:done: /* Close the file descriptor. */ if (fp != NULL && fclose(fp) != 0) { if (ret == 0) ret = errno; __db_err(dbenv, "%s: %s", t->re_source, db_strerror(errno)); } /* Discard the cursor. */ if ((t_ret = dbc->c_close(dbc)) != 0 && ret == 0) ret = t_ret; /* Discard memory allocated to hold the data items. */ if (data.data != NULL) __os_ufree(dbenv, data.data); if (pad != NULL) __os_free(dbenv, pad); if (ret == 0) t->re_modified = 0; return (ret);}/* * __ram_sread -- * Read records from a source file. */static int__ram_sread(dbc, top) DBC *dbc; db_recno_t top;{ BTREE *t; DB *dbp; DBT data, *rdata; db_recno_t recno; size_t len; int ch, ret, was_modified; t = dbc->dbp->bt_internal; dbp = dbc->dbp; was_modified = t->re_modified; if ((ret = __bam_nrecs(dbc, &recno)) != 0) return (ret); /* * Use the record key return memory, it's only a short-term use. * The record data return memory is used by __bam_iitem, which * we'll indirectly call, so use the key so as not to collide. */ len = F_ISSET(dbp, DB_AM_FIXEDLEN) ? t->re_len : 256; rdata = &dbc->my_rkey; if (rdata->ulen < len) { if ((ret = __os_realloc( dbp->dbenv, len, &rdata->data)) != 0) { rdata->ulen = 0; rdata->data = NULL; return (ret); } rdata->ulen = (u_int32_t)len; } memset(&data, 0, sizeof(data)); while (recno < top) { data.data = rdata->data; data.size = 0; if (F_ISSET(dbp, DB_AM_FIXEDLEN)) for (len = t->re_len; len > 0; --len) { if ((ch = getc(t->re_fp)) == EOF) { if (data.size == 0) goto eof; break; } ((u_int8_t *)data.data)[data.size++] = ch; } else for (;;) { if ((ch = getc(t->re_fp)) == EOF) { if (data.size == 0) goto eof; break; } if (ch == t->re_delim) break; ((u_int8_t *)data.data)[data.size++] = ch; if (data.size == rdata->ulen) { if ((ret = __os_realloc(dbp->dbenv, rdata->ulen *= 2, &rdata->data)) != 0) { rdata->ulen = 0; rdata->data = NULL; return (ret); } else data.data = rdata->data; } } /* * Another process may have read this record from the input * file and stored it into the database already, in which * case we don't need to repeat that operation. We detect * this by checking if the last record we've read is greater * or equal to the number of records in the database. */ if (t->re_last >= recno) { ++recno; if ((ret = __ram_add(dbc, &recno, &data, 0, 0)) != 0) goto err; } ++t->re_last; } if (0) {eof: t->re_eof = 1; ret = DB_NOTFOUND; }err: if (!was_modified) t->re_modified = 0; return (ret);}/* * __ram_add -- * Add records into the tree. */static int__ram_add(dbc, recnop, data, flags, bi_flags) DBC *dbc; db_recno_t *recnop; DBT *data; u_int32_t flags, bi_flags;{ BTREE_CURSOR *cp; int exact, ret, stack; cp = (BTREE_CURSOR *)dbc->internal;retry: /* Find the slot for insertion. */ if ((ret = __bam_rsearch(dbc, recnop, S_INSERT | (flags == DB_APPEND ? S_APPEND : 0), 1, &exact)) != 0) return (ret); stack = 1; /* Copy the page into the cursor. */ STACK_TO_CURSOR(cp); /* * The application may modify the data based on the selected record * number. */ if (flags == DB_APPEND && dbc->dbp->db_append_recno != NULL && (ret = dbc->dbp->db_append_recno(dbc->dbp, data, *recnop)) != 0) goto err; /* * Select the arguments for __bam_iitem() and do the insert. If the * key is an exact match, or we're replacing the data item with a * new data item, replace the current item. If the key isn't an exact * match, we're inserting a new key/data pair, before the search * location. */ switch (ret = __bam_iitem(dbc, NULL, data, exact ? DB_CURRENT : DB_BEFORE, bi_flags)) { case 0: /* * Don't adjust anything. * * If we inserted a record, no cursors need adjusting because * the only new record it's possible to insert is at the very * end of the tree. The necessary adjustments to the internal * page counts were made by __bam_iitem(). * * If we overwrote a record, no cursors need adjusting because * future DBcursor->get calls will simply return the underlying * record (there's no adjustment made for the DB_CURRENT flag * when a cursor get operation immediately follows a cursor * delete operation, and the normal adjustment for the DB_NEXT * flag is still correct). */ break; case DB_NEEDSPLIT: /* Discard the stack of pages and split the page. */ (void)__bam_stkrel(dbc, STK_CLRDBC); stack = 0; if ((ret = __bam_split(dbc, recnop, NULL)) != 0) goto err; goto retry; /* NOTREACHED */ default: goto err; }err: if (stack) __bam_stkrel(dbc, STK_CLRDBC); return (ret);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -